Coder Social home page Coder Social logo

twurple / twurple Goto Github PK

View Code? Open in Web Editor NEW
608.0 608.0 102.0 8.39 MB

Interact with Twitch's API, chat and subscribe to events via PubSub and EventSub.

License: MIT License

Shell 0.04% TypeScript 99.88% JavaScript 0.08%
api api-client chat-bot chatbot eventsub pubsub tmi twitch twitch-api twitch-chat webhooks

twurple's Introduction

Twurple

GitHub license npm version PRs welcome

A set of libraries that aims to cover all existing Twitch APIs.

  • Query the Helix API
  • Build a chat bot
  • React to custom redemptions, subscriptions, follows and much more using PubSub and EventSub
  • Do all this without caring about the expiry of your access tokens - we can refresh them automatically

Installation

To add Twurple to your project, just execute:

yarn add @twurple/auth

or using npm:

npm install @twurple/auth

Documentation

A good place to start with this library is the documentation which also includes a complete reference of all classes and interfaces, as well as changes and deprecations between major versions.

Additional packages

The mentioned @twurple/auth package only provides authentication functionality. All the other things are located in separate packages:

If you're getting stuck...

You can join the Twitch API Libraries Discord Server and ask in #twurple for support.

Special thanks

twurple's People

Contributors

1f916 avatar aaronkchsu avatar alcadesign avatar allcontributors[bot] avatar bradnak avatar cavemobster avatar d-fischer avatar daniel0611 avatar dependabot[bot] avatar dfoverdx avatar fractalo avatar freaktechnik avatar gu3st avatar hoodybooti avatar iprodigy avatar jabbink avatar justusfluegel avatar lafiosca avatar lclc98 avatar msikma avatar qjroberts avatar romybron avatar rydann avatar sassyai avatar serycodes avatar stimulcross avatar theca11 avatar trezy avatar zspri avatar zunderscore avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

twurple's Issues

Return subscriptions total from Kracken API getSubscriptions

Feature Request

Similar to #22 I need to get the total number of subscribers a streamer has. Right now, I can query for subscribers information using page and limit query modifiers but I can't see the _total value that is returned from the query.

You can read more about what the endpoint returns here: https://dev.twitch.tv/docs/v5/reference/channels/#get-channel-subscribers

Unfortunately, there is not a Helix endpoint for this information yet. Example of the response data:

{
    "_total": 4,
    "subscriptions": [
        {
            "_id": "e5e2ddc37e74aa9636625e8d2cc2e54648a30418",
            "created_at": "2016-04-06T04:44:31Z",
            "sub_plan": "1000",
            "sub_plan_name": "Channel Subscription (mr_woodchuck)",
            "user": {
                "_id": "89614178",
                "bio": "Twitch staff member who is a heimerdinger main on the road to diamond.",
                "created_at": "2015-04-26T18:45:34Z",
                "display_name": "Mr_Woodchuck",
                "logo": "https://static-cdn.jtvnw.net/jtv_user_pictures/mr_woodchuck-profile_image-a8b10154f47942bc-300x300.jpeg",
                "name": "mr_woodchuck",
                "type": "staff",
                "updated_at": "2017-04-06T00:14:13Z"
            }
        },
    . . .
   ]
}

sub info months is NaN for sub gifts

Bug Report

Code

"use strict";
const twitchChat = require("twitch-chat-client").default,
    client = twitchChat.anonymous(null);

client.connect()
    .then(() => client.waitForRegistration())
    .then(() => client.join('sodapoppin'))
    .then(() => {
        client.onResub((...args) => console.log(...args));
        client.onSub((...args) => console.log(...args));
        client.onSubGift((...args) => console.log(...args));
    })
    .catch(console.error);

Expected behavior

streak has a valid integer value (at least 0...)

Actual Behavior

{ displayName: 'FlauschigesEvoli',
  gifter: undefined,
  gifterDisplayName: undefined,
  gifterGiftCount: undefined,
  plan: '1000',
  planName: 'Channel Subscription (sodapoppin)',
  isPrime: false,
  months: NaN,
  streak: undefined }

is given as sub info, with the following tags:

_tags:
   Map {
     'badges' => 'subscriber/12',
     'color' => '#FF0000',
     'display-name' => 'QC_bajs',
     'emotes' => '',
     'flags' => '',
     'id' => '1c064ad5-70d6-4f5c-a140-2ea167351a8d',
     'login' => 'qc_bajs',
     'mod' => '0',
     'msg-id' => 'subgift',
     'msg-param-months' => '4',
     'msg-param-origin-id' => 'da 39 a3 ee 5e 6b 4b 0d 32 55 bf ef 95 60 18 90 af d8 07 09',
     'msg-param-recipient-display-name' => 'FlauschigesEvoli',
     'msg-param-recipient-id' => '157884754',
     'msg-param-recipient-user-name' => 'flauschigesevoli',
     'msg-param-sender-count' => '14',
     'msg-param-sub-plan-name' => 'Channel Subscription (sodapoppin)',
     'msg-param-sub-plan' => '1000',
     'room-id' => '26301881',
     'subscriber' => '1',
     'system-msg' => 'QC_bajs gifted a Tier 1 sub to FlauschigesEvoli! They have given 14 Gift Subs in the channel!',
     'tmi-sent-ts' => '1553526092433',
     'user-id' => '79214342',
     'user-type' => '' },

msg-param-months is apparently the tenure in the gift case. Yay two tags with the same data but different names for reasons.

Environment

  • Version: 2.1.0
  • Node version: v11.10.0
  • Operating system: elementary OS 5.0

IRC client gets disconnected by the remote when too much data is received

Bug Report

Code

// repeat 1000+ times
chatClient.join(_channelName).then(success);

Expected behavior

My chat client joins all channels and stays in there.

Actual Behavior

All channels are joined, but as soon as Twitch tries to send me around 0.6Mbps of chat messages, Twitch kills the connection (reason 1006 from what I could gather from the logs). I cannot quickly find the exact limit in the documentation of Twitch, but this behaviour is to be expected according to https://discuss.dev.twitch.tv/t/limits-on-irc-channels-join/16247/2
Maybe I missed it in the documentation of this library, but is it possible to create separate connections that handle a couple of channels each, as apparently this send buffer is limited per IRC connection, unlike many other limits (e.g. 15 JOINs/PARTs per 15 seconds which is per IP address).

Coincidentally while adding error handling to restore from this failure I noticed that chatClient.onDisconnect is not implemented yet, or am I missing something. Right now I added code to abuse the "private" chatClient._connection.onDisconnected but I am not entirely sure if that would trigger either as it hasn't happened since.

Environment

  • Version: 2.3.5 (from npmjs)
  • Node version: 11.x (dockerhub node:11-alpine)
  • Operating system: Alpine (docker)

New Notice Found: msg_banned

Feature Request

Found this one come across my logs today

  • msg_banned

Looks like it comes through when a user tries to message a channel they are banned in

API docs

Are there any docs anywhere for the methods found in this package? Or should I just browse the code itself?

Error while using TwitchClient.withCredentials and checking for Subscriptions

Bug Report

When using TwitchClient.withCredentials and calling getSubscriptions or getSubscriptionForUser (and I tested with Kraken too with same credentials, and Kraken scopes). It returns no error object at all in the catch.

Note: I tested with the request package with same access tokens in the same session, just one line different, and it returns correctly.

Code

    let errors = [];
    let dailyPoints=0;

    let channelTwitchClient;
    let appTwitchClient;
    let channelSettings;
    if(!channelTwitchClient){
      let accessTokenCheck = await TwitchClient.getTokenInfo(APIKeys.twitchEXTClientID,<ACCESS_TOKEN>);
      errors.push({name:'accessTokenCheck',data:accessTokenCheck});
      if(accessTokenCheck){
        let [chanTwitchClient,chanTwitchClientError] = await handle(TwitchClient.withCredentials(APIKeys.twitchEXTClientID,<ACCESS_TOKEN>));
        if(chanTwitchClientError) errors.push({name:'chanTwitchClientError',error:chanTwitchClientError});
        if(channelTwitchClient && !chanTwitchClientError){
          channelTwitchClient=chanTwitchClient;
        }
      }
    }

    if(!appTwitchClient) {
      let [apTwitchClient,apTwitchClientError] = await handle(TwitchClient.withCredentials(APIKeys.twitchEXTClientID, <APP_ACCESS_TOKEN>));
      if(apTwitchClientError) errors.push({name:'apTwitchClientError',error:apTwitchClientError});
      if(apTwitchClient && !apTwitchClientError){
        appTwitchClient=apTwitchClient;
      }
    }
    if(!channelTwitchClient && appTwitchClient){
      channelTwitchClient=appTwitchClient;
    }
    let [follow,followError] = await handle(channelTwitchClient.kraken.users.getFollowedChannel(req.query.userID,req.query.channelID));
    channelTwitchClient.helix.subscriptions.getSubscriptions(req.query.channelID)
      .then(data=>{
        errors.push({name:'getSubscriptionForUser.then',data:data})
      })
      .catch(error=>{
        errors.push({name:'getSubscriptionForUser.catch',error:error})
      });
    let [subscription,subscriptionError] = await handle(channelTwitchClient.helix.subscriptions.getSubscriptionForUser(req.query.channelID,req.query.userID));
    //let [subscription,subscriptionError] = await handle(channelTwitchClient.kraken.channels.getChannelSubscriptionByUser(req.query.channelID,req.query.userID));
    //let [subscription,subscriptionError] = await handle(appTwitchClient.kraken.channels.getChannelSubscriptionByUser(req.query.channelID,req.query.userID));
    let [moderator,moderatorError] = await handle(channelTwitchClient.helix.moderation.getModerators(req.query.channelID,{userId:req.query.userID}));
    let [shadowFlipBotSub,shadowFlipBotSubError] = await handle(appTwitchClient.helix.subscriptions.getSubscriptionForUser('435188356',req.query.userID));
    //let [shadowFlipsSub,shadowFlipsSubError] = await handle(appTwitchClient.helix.subscriptions.getSubscriptionForUser('485311079',userID));

    if(!channelSettings){
      let [chanSettings,chanSettingsError] = await handle(getChannelSettings(req.query.channelID));
      if(chanSettingsError) errors.push(chanSettingsError);
      if(chanSettings && !chanSettingsError){
        channelSettings=chanSettings;
      }
    }

    if(channelSettings){
      dailyPoints += channelSettings.settings.goLivePoints.base * channelSettings.pointSKUs.flip.multiplier;
      //If they are following
      if(follow){
        dailyPoints += channelSettings.settings.goLivePoints.follow * channelSettings.pointSKUs.flip.multiplier;
      }
      //If notifications are on
      if(follow && follow.hasNotifications){
        dailyPoints += channelSettings.settings.goLivePoints.notification * channelSettings.pointSKUs.flip.multiplier;
      }
      //If Subscribed
      if(subscription && subscription.tier){
        dailyPoints+=channelSettings.settings.goLivePoints.subscription[subscription.tier] * channelSettings.pointSKUs.flip.multiplier;
      }
      if(shadowFlipBotSub && shadowFlipBotSub.tier){
        dailyPoints+=channelSettings.settings.goLivePoints.subscription[shadowFlipBotSub.tier] * channelSettings.pointSKUs.flip.multiplier;
      }

      //if(shadowFlipsSub && shadowFlipsSub.tier){
      //  dailyPoints+=channelSettings.settings.goLivePoints.subscription[botSub['sub_plan']] * channelSettings.pointSKUs.flip.multiplier;
      //}

      if(moderator){
        dailyPoints += channelSettings.pointSKUs.flip.multiplier;
      }
    }
    if(followError) errors.push({name:'followError',error:followError});
    if(subscriptionError) errors.push({name:'subscriptionError',error:subscriptionError});
    if(shadowFlipBotSubError) errors.push({name:'shadowFlipBotSubError',error:shadowFlipBotSubError});
    if(moderatorError) errors.push({name:'moderatorError',error:moderatorError});

    if(errors.length>0) pushError(req.query.channelID,createErrorObject(req.query.channelID,req.query.userID,errors,'testBotGoLivePointAmount')).catch(()=>{});
    if(dailyPoints>=0){
      console.log(JSON.stringify(dailyPoints) + ' - ' + JSON.stringify(errors));
    }
    else{
      console.log('Something went wrong - ' + JSON.stringify(errors));
    }

handle is just a async wrapper to grab the errors easily.

export const handle = (promise) => {
  return promise
    .then(data => ([data, undefined]))
    .catch(error => Promise.resolve([undefined, error]));
};

The results from the token check is

            {
                "client_id": "f32p3no3oxomqviy35liavolqb4gyo",
                "login": "wlg3r",
                "scopes": [
                    "channel:read:subscriptions",
                    "channel_check_subscription",
                    "channel_read",
                    "channel_subscriptions"
                ],
                "user_id": "145796834",
                "expires_in": 816929
            }

I left out moderation to check a call with the wrong scope, and the call to moderators returns nothing, just like subs.

Expected behavior

Returns subscription object.

Actual Behavior

No object, and no error object.

Environment

  • Version: 3.7.1
  • Node version: 10 and 8

Clips API

Will the Clips API from v5 ever be implemented? Or is there a way through this library to make custom API calls, such as for Clips?

chatClient.onRegister is not a function

Bug Report

Getting chatClient.onRegister is not a function for following the basic "connecting to chat" docs

Code

const twitchClient = Twitch.withClientCredentials(
        process.env.TWITCH_CLIENT_ID || '',
        process.env.TWITCH_CLIENT_SECRET || '
');
const chatClient = await ChatClient.forTwitchClient(twitchClient);
chatClient.onRegister(() => {});

Expected behavior

connecting to chat

Actual Behavior

chatClient.onRegister is not a function

Environment

Tried with latest, and then the new pre-releases, same thing.
Node: 13.1.0
OS: Ubuntu

Failing to get value at "ChatBadgeList.badgeSetNames".

Bug Report

Cannot convert undefined or null to object error when call globalBadges.badgeSetNames.
I am immature, but tried to send PR. #28

Code

const globalBadges = await twitchClient.badges.getGlobalBadges();
globalBadges.badgeSetNames.forEach(s => {
  const badgeSet = globalBadges.getBadgeSet(s);
  badgeSet.versionNames.forEach(v => {
    const version = badgeSet.getVersion(v);
    console.debug(JSON.stringify({
      imageUrl: version.getImageUrl(1),
      desc: version.description,
      clickUrl: version.clickUrl,
      clickAction: version.clickAction,
      title: version.title,
    }));
  });
});

Expected behavior

call ChatBadgeList.badgeSetNames If normal return badge set names(string[]).

Actual Behavior

but, ChatBadgeList.badgeSetNames :

return Object.keys(this._data.versions);

Isn't it correct to return a list of badge names rather than a list of versions?

I think the structure is as follows.

{
  broadcaster: {
    versions: {
      "1": {
        title: ~~,
        description: ~~,
        ...
      }
      ...
    }
  }
  ...
}

Environment

  • Version: 2.0.0
  • Browser version: Google Chrome 72.0.3626.121
  • Operating system: OSX Mojave

subscribeToStreamChanges returns invalid JSON

Bug Report

subscribeToStreamChanges returns invalid json response body at https://api.twitch.tv/helix/webhooks/hub reason: Unexpected end of JSON input

Code

const id = "37768708"; // My user;
let listener = await WebHookListener.create(twitchClient, {port: 8081});
listener.listen();
listener.subscribeToStreamChanges(id, (stream) => {
    console.log(id, stream);
});

Expected behavior

No error received.

Actual Behavior

An error is thrown.

Environment

  • Version: 2.2.7
  • Node version: v8.15.1
  • Operating system: Windows 10

helix/streams errors

Bug Report

Environment

  • Version: 1.3.5
  • Node version: v8.15.0
  • Operating system: Ubuntu 16.04.5 LTS

Errors:

 Caught unhandledRejection:  { RequestError: Error: getaddrinfo EAI_AGAIN api.twitch.tv:443
     at new RequestError (/twitch/node_modules/request-promise-native/node_modules/request-promise-core/lib/errors.js:14:15)
     at Request.plumbing.callback (/twitch/node_modules/request-promise-native/node_modules/request-promise-core/lib/plumbing.js:87:29)
     at Request.RP$callback [as _callback] (/twitch/node_modules/request-promise-native/node_modules/request-promise-core/lib/plumbing.js:46:31)
     at self.callback (/twitch/node_modules/request/request.js:185:22)
     at emitOne (events.js:116:13)
     at Request.emit (events.js:211:7)
     at Request.onRequestError (/twitch/node_modules/request/request.js:881:8)
     at emitOne (events.js:116:13)
     at ClientRequest.emit (events.js:211:7)
     at TLSSocket.socketErrorListener (_http_client.js:401:9)
     at emitOne (events.js:116:13)
     at TLSSocket.emit (events.js:211:7)
     at emitErrorNT (internal/streams/destroy.js:66:8)
     at args.(anonymous function) (/usr/lib/node_modules/pm2/node_modules/event-loop-inspector/index.js:138:29)
     at _combinedTickCallback (internal/process/next_tick.js:139:11)
     at process._tickCallback (internal/process/next_tick.js:181:9)
   name: 'RequestError',
   message: 'Error: getaddrinfo EAI_AGAIN api.twitch.tv:443',
   cause:
    { Error: getaddrinfo EAI_AGAIN api.twitch.tv:443
     at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:67:26)
      errno: 'EAI_AGAIN',
      code: 'EAI_AGAIN',
      syscall: 'getaddrinfo',
      hostname: 'api.twitch.tv',
      host: 'api.twitch.tv',
      port: 443 },
   error:
    { Error: getaddrinfo EAI_AGAIN api.twitch.tv:443
     at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:67:26)
      errno: 'EAI_AGAIN',
      code: 'EAI_AGAIN',
      syscall: 'getaddrinfo',
      hostname: 'api.twitch.tv',
      host: 'api.twitch.tv',
      port: 443 },
   options:
    { url: 'https://api.twitch.tv/helix/streams',
      headers:
       { Accept: 'application/vnd.twitchtv.v5+json',
         'Client-ID': '***',
         Authorization: 'Bearer ***' },
      qs:
       { community_id: undefined,
         game_id: undefined,
         language: undefined,
         type: undefined,
         user_id: undefined,
         user_login: '***',
         after: undefined,
         first: '100' },
      qsStringifyOptions: { arrayFormat: 'repeat' },
      json: true,
      gzip: true,
      callback: [Function: RP$callback],
      transform: undefined,
      simple: true,
      resolveWithFullResponse: false,
      transform2xxOnly: false },
   response: undefined }
 Caught unhandledRejection:  { RequestError: Error: Parse Error
     at new RequestError (/twitch/node_modules/request-promise-native/node_modules/request-promise-core/lib/errors.js:14:15)
     at Request.plumbing.callback (/twitch/node_modules/request-promise-native/node_modules/request-promise-core/lib/plumbing.js:87:29)
     at Request.RP$callback [as _callback] (/twitch/node_modules/request-promise-native/node_modules/request-promise-core/lib/plumbing.js:46:31)
     at self.callback (/twitch/node_modules/request/request.js:185:22)
     at emitOne (events.js:116:13)
     at Request.emit (events.js:211:7)
     at Request.onRequestError (/twitch/node_modules/request/request.js:881:8)
     at emitOne (events.js:116:13)
     at ClientRequest.emit (events.js:211:7)
     at TLSSocket.socketOnData (_http_client.js:459:9)
     at emitOne (events.js:116:13)
     at TLSSocket.emit (events.js:211:7)
     at addChunk (_stream_readable.js:263:12)
     at readableAddChunk (_stream_readable.js:250:11)
     at TLSSocket.Readable.push (_stream_readable.js:208:10)
     at TLSWrap.onread (net.js:601:20)
   name: 'RequestError',
   message: 'Error: Parse Error',
   cause: { Error: Parse Error
     at TLSSocket.socketOnData (_http_client.js:454:20)
     at emitOne (events.js:116:13)
     at TLSSocket.emit (events.js:211:7)
     at addChunk (_stream_readable.js:263:12)
     at readableAddChunk (_stream_readable.js:250:11)
     at TLSSocket.Readable.push (_stream_readable.js:208:10)
     at TLSWrap.onread (net.js:601:20) bytesParsed: 0, code: 'HPE_INVALID_CONSTANT' },
   error: { Error: Parse Error
     at TLSSocket.socketOnData (_http_client.js:454:20)
     at emitOne (events.js:116:13)
     at TLSSocket.emit (events.js:211:7)
     at addChunk (_stream_readable.js:263:12)
     at readableAddChunk (_stream_readable.js:250:11)
     at TLSSocket.Readable.push (_stream_readable.js:208:10)
     at TLSWrap.onread (net.js:601:20) bytesParsed: 0, code: 'HPE_INVALID_CONSTANT' },
   options:
    { url: 'https://api.twitch.tv/helix/streams',
      headers:
       { Accept: 'application/vnd.twitchtv.v5+json',
         'Client-ID': '****',
         Authorization: 'Bearer ****' },
      qs:
       { community_id: undefined,
         game_id: undefined,
         language: undefined,
         type: undefined,
         user_id: undefined,
         user_login: '***',
         after: undefined,
         first: '100' },
      qsStringifyOptions: { arrayFormat: 'repeat' },
      json: true,
      gzip: true,
      callback: [Function: RP$callback],
      transform: undefined,
      simple: true,
      resolveWithFullResponse: false,
      transform2xxOnly: false },
   response: undefined }

Port from Kraken to Helix

When Kraken gets phased out, and assuming that Helix adopts all the endpoints that Kraken has by the end of this year, then will this package seamlessly migrate from Kraken to Helix in the backend?

Meaning, if I make a call to streams.getAllLiveStreams, which I assume uses Kraken for now, then will it eventually use Helix when that's available, and all I have to do on my end is update my version of this package? Assuming they have the same response of course; otherwise, I guess this package could just map the new keys to the old ones.

And is there a way to manually switch between the two APIs?

Wrong type in TwitchChatClient onRitual method signature

Bug Report

There's a typo here in the TwitchChatClient onRitual method signature where an argument has the type ChatRaidInfo instead of ChatRitualInfo. I've created a simple PR to fix it, which I will link to this issue.

Code

n/a

Expected behavior

The type of the parameter ritualInfo should be ChatRitualInfo.

Actual Behavior

The type of the parameter ritualInfo is ChatRaidInfo.

Environment

n/a

Normalize `_id` and `_data`

Any chance that _id and _data could be normalized so that id and data would also work?

Or is this package intending on 100% mirroring the Twitch API response with these values? I don't understand what the purpose of the underscore is, in both this package and the API response?

Access Tokens are always required

Bug Report

Code

Initially was using withClientCredentials but got told I should be using withCredentials

    const twitchClient = await TwitchClient.withCredentials("<clientId>");
    const user = await twitchClient.helix.users.getUserByName(userName);

Expected behavior

Don't require a token for making requests to unscoped calls.

Actual Behavior

If statement is causing a token to always be required.

The initial if statement is always true as its either an empty array or contains scopes
https://github.com/d-fischer/twitch/blob/master/packages/twitch/src/Auth/StaticAuthProvider.ts#L46

Passes the require scopes or an empty array
https://github.com/d-fischer/twitch/blob/master/packages/twitch/src/TwitchClient.ts#L387

Environment

  • Version: 2.2.3
  • Node version: v10.15.1
  • Browser version: Firefox 66.0.5
  • Operating system: Windows 10

onHost can return NaN for number of viewers

Bug Report

When connecting to a channel that is already hosting another channel, the onHost callback returns a value of NaN for the number of viewers rather than undefined.

This is due to a possible change in the notices that Twitch sends. Specifically, when connecting to a channel that is already hosting another channel, Twitch sends a notice in the form of targetchannel -, with a hyphen in the viewer count position.

Code

const twitchChat = await TwitchChatClient.forTwitchClient(twitchClient);
twitchChat.onHost((channel, target, viewers) => {
    console.log(`viewers ${isNaN(viewers) ? 'isNaN' : 'is a number'}`);
});

Expected behavior

When connecting to a channel that is already hosting another channel, onHost should receive an undefined value for viewers.

Actual Behavior

When connecting to a channel that is already hosting another channel, onHost receives NaN for viewers.

Environment

n/a

onRefresh function not calling and refresh_token is empty in getAccessToken()

Bug Report

Code

const TwitchClient = require('twitch').default;

const TEST_CASE = 1;

const clientId = 'uo6dggojyb8d6soh92zknwmi5ej1q2';
const authToken = 'qwertyuiopasdf1234567890';
const clientSecret = 'nyo51xcdrerl8z9m56w9w6wg';
const refreshToken = 'uo6dggojyb8d6soh92zknwmi5ej1q2uo6dggojyb8d6soh92zknwmi5ej1q2';

TwitchClient.withCredentials(clientId, authToken, undefined, {
	clientSecret,
	refreshToken,
	onRefresh: (token) => {
		console.log('onRefresh', token);
	}
}).then(twitchClient => {
	if (TEST_CASE === 1) {
		twitchClient.getAccessToken().then(token => {
			console.log('getAccessToken', token);
		});
	} else if (TEST_CASE === 2) {
		twitchClient.refreshAccessToken().then(token => {
			console.log('refreshAccessToken', token);
		});
	}
});

With TEST_CASE = 1 and onRefresh function simply not calling, and getAccessToken() gives new token, but refresh_token key is empty.
But with TEST_CASE = 2, callback works as it should and refresh_token present in response.

And yes, acess_token that I pass to withCredentials not working.

Environment

  • Version: 3.5.6, 3.7.0
  • Node version: v12.9.1, v12.14.1, v13.7.0
  • Operating system: Windows 10, Ubuntu 18.04

Add a whisper method

Feature Request

Given other methods like say and action are implemented, the lack of a whisper method seems like an omission. Right now I believe you have to use the lower level send:

const whisper = (user: string, message: string) =>
  bot.send(`PRIVMSG #jtv :/w ${user} ${message}`)

That would round off the methods to send messages:

bot.say(channel, 'hello')
bot.action(channel, 'waves.')
bot.whisper(channel, 'hello again, but privately')

Creating Client with invalid (but potentially refreshable token) fails at fetching scopes

Bug Report

Code

        const refreshConfig = {
            clientSecret: Config.getTwitchClientSecret(),
            refreshToken: AccessTokenStore.getTwitchRefreshToken(),
            onRefresh: _tokenRefreshHandler /* function that handles storing tokens */
        };

        this.twitch = TwitchClient.withCredentials(Config.getTwitchClientId(),
            AccessTokenStore.getTwitchToken(),
            undefined, refreshConfig)

Expected behavior

In the withCredentials call, I think it should refreshToken before fetching scopes if the token is expired

Actual Behavior

withCredentials makes the assumption that the access token is not expired. If it's expired, it fails when attempting to fetch scopes for the given key (as the key is expired). Manually refreshing the key with the token refresh information works, so it should be possible.

Environment

  • Version: 2.1.1
  • Node version: 8.11
  • Operating system: Linux Kernel 4.15

Getting followers total count

Hey,

Is there any way to get the followers total count of a stream? I was looking for that but only found the count of users that the streamer follow.

Getting game by id result in error

Bug Report

I'm trying to get game data by id and get an error as result

Code

// Tryng with id = 493057 (PUBG)
async findGame(id: string) {
    // TWITCH_CLIENT_ID is correctly setted
    this.twitchClient = TwitchClient.withCredentials(process.env.TWITCH_CLIENT_ID || '')
    const gameData = await this.twitchClient.helix.games.getGameById(id)
    return gameData
}

Expected behavior

Getting HelixGame Object

Actual Behavior

I get this error :

TypeError: Cannot read property 'cursor' of undefined
    at HelixPaginatedRequest.<anonymous> (~/bot/node_modules/twitch/src/API/Helix/HelixPaginatedRequest.ts:37:43)
    at step (~/bot/node_modules/tslib/tslib.js:133:27)
    at Object.next (~/bot/node_modules/tslib/tslib.js:114:57)
    at fulfilled (~/bot/node_modules/tslib/tslib.js:104:62)
    at process._tickCallback (internal/process/next_tick.js:68:7)

Environment

  • Version: 1.0.5
  • Node version: 10.13.0
  • Operating system: Window 10 Pro

Error on `users.getFollowedChannels`

When I use twitchClient.users.getFollowedChannels('ninja'), I get UnhandledPromiseRejectionWarning: StatusCodeError: 400 - {"error":"Bad Request","status":400,"message":"The parameter \"user_id\" was malformed: the value must match the regular expression /^[0-9]*$/"}.

"TypeError: TwitchClient.withCredentials is not a function"

I have a very simple script, just to test this package. Yet it doesn't work. I'm getting TypeError: TwitchClient.withCredentials is not a function.

Only twitch is installed in package.json.
I have this:

const TwitchClient = require('twitch');

const accessToken = '...';
const clientId = '...';

const twitchClient = TwitchClient.withCredentials(clientId, accessToken);

Reading property scope from an AccessToken throws a TypeError

Bug Report

When reading the property scope from an AccessToken received with the OAuth Authorization Code Flow it throws an error.

The Twitch API returns an array for the scope property while the code assumes it's a string and runs scope.split(' ') which results in an error because the function split doesn't exist on arrays.

EDIT: After looking a bit into the API Docs, it seems like Twitch always returns an array except for the case when a token is refreshed. I did not test out the refresh case yet, but it should not be overlooked when fixing this.

EDIT2: Just tested the refresh case. Also returns an array of scopes for me. So the Twitch docs are also not 100% up to date in that case.

Code

const express = require('express');
const twitch = require('twitch');

const app = express();

const clientID = '<Your client ID>';
const clientSecret = '<Your client Secret>';
const redirectUri = 'http://localhost:8381/twitch/authorize/callback'

const scopes = ['user_read', 'channel_editor'];
// Also try with no scopes. Gives another error
// const scopes = [];

app.get('/twitch/authorize', (req, res) => {
	res.redirect(`https://id.twitch.tv/oauth2/authorize?client_id=${clientID}&redirect_uri=${redirectUri}&response_type=code&scope=${scopes.join(' ')}&force_verify=true`)
});

app.get('/twitch/authorize/callback', async (req, res) => {
	const token = await twitch.default.getAccessToken(clientID, clientSecret, req.query.code, redirectUri);

	try {
		res.send(token.scope);
	} catch (e) {
		console.log(e);
		res.send(e.stack);
	}
});

app.listen(8381);
console.log('Started. Open http://localhost:8381/twitch/authorize');

Expected behavior

It returns the array of scopes that was returned by the Twitch API.

Actual Behavior

When reading the property scope from an AccessToken received with the OAuth Authorization Code Flow it throws the following error:

TypeError: this._data.scope.split is not a function
    at AccessToken.get [as scope] (/home/<redacted>/Workspaces/<redacted>/node_modules/twitch/lib/API/AccessToken.js:63:37)

Environment

  • Version: 3.4.4
  • Node version: 10.15.1
  • Operating system: Linux (Ubuntu 18.04.3 LTS)

Cannot select a new Twitch account in Electron Provider

Bug Report

There is currently no way for a logged in user (via Electron Provider) to select a new/different account to log into. Due to cookies being stored in the Electron session, should a user attempt to log in again, no popup appears for them to select an account or choose different account.

Expected behavior

If has already logged in via the Electron provider, if they try to log in again, it should show the Twitch log in window allowing them to choose a different account.

Actual Behavior

No window pops up, but instead a token is instantly resolved.

Environment

  • Version: ^3.5.6

New UserNotice Found: rewardgift

Feature Request

โš ๏ธ twitch-chat WARNING Unrecognized usernotice ID: rewardgift

pretty sure has to do with the free winter emotes that the community gets when someone subs, but i don't have much data past that

Helix GetFollows does not store the total results returned from the Twitch API

Feature Request

The Twitch API includes a total results property to give developers an idea of how many pages there are in total. Unfortunately this value is not stored or made accessible in the current library. I find myself in a similar situation now where I want to query for followers but only care about the total number of followers (not the individual follower's information). In this case, I'd like to be able to set the results limit to 1, and grab the total value from the first query (essentially not using the pagination at all).

Here's an example of the response from Twitch:

{
   "total": 12345,
   "data":
   [
      {
         "from_id": "171003792",
         "from_name": "IIIsutha067III",
         "to_id": "23161357",
         "to_name": "LIRIK",
         "followed_at": "2017-08-22T22:55:24Z"
      },
      {
         "from_id": "113627897",
         "from_name": "Birdman616",
         "to_id": "23161357",
         "to_name": "LIRIK",
         "followed_at": "2017-08-22T22:55:04Z"
      },
      ...
   ],
   "pagination":{
     "cursor": "eyJiIjpudWxsLCJhIjoiMTUwMzQ0MTc3NjQyNDQyMjAwMCJ9"
   }
}```

Some tests?

Feature Request

Well I think it could be nice to have a way to test if our listeners are working, some way to send fake data...

Can't specify stream type for getFollowedStreams()

I can't seem to specify the stream type for getFollowedStreams(). I'm trying to use strings and the enums, but I can't seem to figure it out.

Does it accept either string or enum? Or how do I use an enum for it?

I tried 'live', twitch.StreamType.Live, etc.

ChatClient UserInfo.userName is "tmi.twitch.tv"

Bug Report

ChatClient's UserInfo isn't working correctly. For example msg.userInfo.userName returns tmi.twitch.tv instead of the actual login.

Code

chatClient.onSub((channel, user, subInfo, msg) => {
    console.log(msg.userInfo.userName); // "tmi.twitch.tv"
});

Example raw IRC:

'@badge-info=;badges=premium/1;color=;display-name=LoginValue;emotes=;flags=;id=12345678-1234-4321-9876-0123456789ab;login=loginvalue;mod=0;msg-id=sub;msg-param-cumulative-months=1;msg-param-months=0;msg-param-should-share-streak=0;msg-param-sub-plan-name=Channel\\sSubscription\\s(somechannelname);msg-param-sub-plan=Prime;room-id=123456789;subscriber=1;system-msg=LoginValue\\ssubscribed\\swith\\sTwitch\\sPrime.;tmi-sent-ts=1580000000000;user-id=987654321;user-type= :tmi.twitch.tv USERNOTICE #somechannelname'

Expected behavior

You should expect "loginvalue" instead of "tmi.twitch.tv".

Actual Behavior

The prefix is not fully populated with the correct value for the user, so you get tmi.twitch.tv instead of something else. (It's just tmi.twitch.tv instead of [email protected] or whatever)

/* ... */
_prefix: { nick: 'tmi.twitch.tv' },
/* ... */

Environment

  • Version: [email protected]
  • Node version: 13.8.0
  • Operating system: Windows 10, using Ubuntu 18.04 via WSL

TwitchChatClient onTimeout does not provide reason

Bug Report

Assuming that onTimeout worked in the past, it appears to me that something has changed about the chat message format that communicates chat timeouts. The onTimeout method does not provide a value for reason (the third argument), and the raw output from the underlying IRC client does not appear to contain the timeout reason.

Code

Nothing fancy, just set up a TwitchChatClient on a channel and listen for timeout events:

twitchChat.onTimeout((channel, user, reason, duration) => {
    console.log(JSON.stringify({
        channel,
        user,
        reason,
        duration,
    }));
})

Or hook directly into the ChatClear message like the library does:

import ClearChat from 'twitch-chat-client/lib/Capabilities/TwitchCommands/MessageTypes/ClearChat';

twitchChat.onMessage(ClearChat, (args) => {
    console.log(`CLEAR CHAT: ${util.inspect(args, { showHidden: true, depth: null })}`);
});

Expected behavior

When I type /timeout foobar 10 this is a test into Twitch chat, I expect the onTimeout event to be called with a string value of 'this is a test' for reason.

Actual Behavior

Instead, reason seems to be undefined.

Looking at the code, it seems the reason was previously stored in a tag called ban-reason, but I do not see this tag when testing:

ClearChat {
  _params:
   [ { value: '#lafiosca', trailing: false },
     { value: 'foobar', trailing: true },
     [length]: 2 ],
  _serverProperties:
   { channelTypes: '#&',
     supportedUserModes: 'iwso',
     supportedChannelModes:
      { prefix: 'ov',
        list: 'b',
        alwaysWithParam: 'ovk',
        paramWhenSet: 'l',
        noParam: 'imnpst' },
     prefixes:
      [ { modeChar: 'v', prefix: '+' },
        { modeChar: 'o', prefix: '@' },
        [length]: 2 ] },
  _command: 'CLEARCHAT',
  _tags:
   Map {
     'ban-duration' => '10',
     'room-id' => '81918359',
     'target-user-id' => '16231',
     'tmi-sent-ts' => '1555130783309',
     [size]: 4 },
  _prefix: { nick: 'tmi.twitch.tv' },
  _raw: '@ban-duration=10;room-id=81918359;target-user-id=16231;tmi-sent-ts=1555130783309 :tmi.twitch.tv CLEARCHAT #lafiosca :foobar',
  _parsedParams:
   { channel: { value: '#lafiosca', trailing: false },
     user: { value: 'foobar', trailing: true } } }

I wonder if it is even still possible for us to get this value. The Twitch dashboard chat client seems to show the message (at least for the person who runs the timeout command), which makes me think it might be communicated some other way now.

Named args

Consider allowing named args rather than just positional arguments, for most methods.

For instance, users.getFollowedChannels(123, null, 100) could be users.getFollowedChannels(123, { limit: 100 } or users.getFollowedChannels({ user: 123, limit: 100 }) etc.

Webhook for transaction created

Feature Request

Hey,

I'm looking at creating twitch extensions and one of the tricky things around them is knowing when bits have come through from the extension and double checking if they fail. The webhook for Transaction Created is what we would need to use and it appears that this package doesn't currently have the functionality to subscribe to this event.

https://dev.twitch.tv/docs/api/webhooks-reference#topic-extension-transaction-created

Willing to contribute to get this in, but will need some guidance on what needs doing.

twitch-webhooks documentation outdated

The documentation refers to subscribeToUserFollowsFrom. However in the code there's actually subscribeToFollowsFromUser and subscribeToFollowsToUser.

`channel.id` is number or string, depending on the method

For kraken.users.getFollowedChannels, the channel.id value for each returned channel is string.

For kraken.streams.getFollowedStreams, channel.id is a number.

I think they should be the same type, regardless of which one is chosen.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.