Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions nodecg-io-twitch-pubsub/extension/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { NodeCG } from "nodecg/types/server";
import { emptySuccess, Result, success } from "nodecg-io-core/extension/utils/result";
import { ServiceBundle } from "nodecg-io-core/extension/serviceBundle";
import { PubSubServiceClient } from "./pubSubClient";

export { PubSubServiceClient } from "./pubSubClient";

export interface PubSubServiceConfig {
oauthKey: string;
}

module.exports = (nodecg: NodeCG) => {
new TwitchPubSubService(nodecg, "twitch-pubsub", __dirname, "../pubsub-schema.json").register();
};

class TwitchPubSubService extends ServiceBundle<PubSubServiceConfig, PubSubServiceClient> {
async validateConfig(config: PubSubServiceConfig): Promise<Result<void>> {
await PubSubServiceClient.getTokenInfo(config); // This will throw a error if the token is invalid
return emptySuccess();
}

async createClient(config: PubSubServiceConfig): Promise<Result<PubSubServiceClient>> {
const client = await PubSubServiceClient.createClient(config);

return success(client);
}

stopClient(_: PubSubServiceClient): void {
// Not possible
}
}
89 changes: 89 additions & 0 deletions nodecg-io-twitch-pubsub/extension/pubSubClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { ServiceClient } from "nodecg-io-core/extension/types";
import { getTokenInfo, StaticAuthProvider, TokenInfo } from "twitch-auth";
import { PubSubServiceConfig } from "./index";
import {
PubSubBitsBadgeUnlockMessage,
PubSubBitsMessage,
PubSubChatModActionMessage,
PubSubClient,
PubSubListener,
PubSubRedemptionMessage,
PubSubSubscriptionMessage,
PubSubWhisperMessage,
} from "twitch-pubsub-client";
import { ApiClient } from "twitch";

export class PubSubServiceClient implements ServiceClient<PubSubClient> {
constructor(private client: PubSubClient, private userid: string) {}

/**
* Creates a instance of TwitchServiceClient using the credentials from the passed config.
*/
static async createClient(cfg: PubSubServiceConfig): Promise<PubSubServiceClient> {
// Create a twitch authentication provider
const tokenInfo = await PubSubServiceClient.getTokenInfo(cfg);
const authProvider = new StaticAuthProvider(tokenInfo.clientId, this.normalizeToken(cfg), tokenInfo.scopes);

// Create the actual chat client and connect
const apiClient = new ApiClient({ authProvider });
const pubSubClient = new PubSubClient();
const userID = await pubSubClient.registerUserListener(apiClient);

return new PubSubServiceClient(pubSubClient, userID);
}

/**
* Gets the token info for the passed config.
*/
static async getTokenInfo(cfg: PubSubServiceConfig): Promise<TokenInfo> {
return await getTokenInfo(this.normalizeToken(cfg));
}

/**
* Strips any "oauth:" before the token away, because the client needs the token without it.
*/
static normalizeToken(cfg: PubSubServiceConfig): string {
return cfg.oauthKey.replace("oauth:", "");
}

getNativeClient(): PubSubClient {
return this.client;
}

getUserID(): string {
return this.userid;
}

async onRedemption(
callback: (message: PubSubRedemptionMessage) => void,
): Promise<PubSubListener<PubSubRedemptionMessage>> {
return await this.client.onRedemption(this.userid, callback);
}

async onSubscription(
callback: (message: PubSubSubscriptionMessage) => void,
): Promise<PubSubListener<PubSubSubscriptionMessage>> {
return await this.client.onSubscription(this.userid, callback);
}

async onBits(callback: (message: PubSubBitsMessage) => void): Promise<PubSubListener<PubSubBitsMessage>> {
return await this.client.onBits(this.userid, callback);
}

async onBitsBadgeUnlock(
callback: (message: PubSubBitsBadgeUnlockMessage) => void,
): Promise<PubSubListener<PubSubBitsBadgeUnlockMessage>> {
return await this.client.onBitsBadgeUnlock(this.userid, callback);
}

async onModAction(
channelID: string,
callback: (message: PubSubChatModActionMessage) => void,
): Promise<PubSubListener<PubSubChatModActionMessage>> {
return await this.client.onModAction(this.userid, channelID, callback);
}

async onWhisper(callback: (message: PubSubWhisperMessage) => void): Promise<PubSubListener<PubSubWhisperMessage>> {
return await this.client.onWhisper(this.userid, callback);
}
}
43 changes: 43 additions & 0 deletions nodecg-io-twitch-pubsub/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "nodecg-io-twitch-pubsub",
"version": "0.1.0",
"description": "Allows access to the Twitch PubSub API.",
"homepage": "https://nodecg.io/samples/twitch-pubsub",
"author": {
"name": "derNiklaas",
"url": "https://github.com/derNiklaas"
},
"repository": {
"type": "git",
"url": "https://github.com/codeoverflow-org/nodecg-io.git",
"directory": "nodecg-io-twitch-pubsub"
},
"main": "extension",
"scripts": {
"build": "tsc -b",
"watch": "tsc -b -w",
"clean": "tsc -b --clean"
},
"keywords": [
"nodecg-io",
"nodecg-bundle"
],
"nodecg": {
"compatibleRange": "^1.1.1",
"bundleDependencies": {
"nodecg-io-core": "^0.1.0"
}
},
"license": "MIT",
"devDependencies": {
"@types/node": "^14.6.4",
"nodecg": "^1.6.1",
"typescript": "^4.0.2"
},
"dependencies": {
"nodecg-io-core": "^0.1.0",
"twitch": "^4.3.6",
"twitch-pubsub-client": "^4.3.6",
"ws": "^7.4.1"
}
}
12 changes: 12 additions & 0 deletions nodecg-io-twitch-pubsub/pubsub-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"oauthKey": {
"type": "string",
"description": "A OAuth2 key for the twitch api."
}
},
"required": ["oauthKey"]
}
3 changes: 3 additions & 0 deletions nodecg-io-twitch-pubsub/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../tsconfig.common.json"
}
27 changes: 27 additions & 0 deletions samples/pubsubsample/extension/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { NodeCG } from "nodecg/types/server";
import { PubSubServiceClient } from "nodecg-io-twitch-pubsub/extension";
import { requireService } from "nodecg-io-core/extension/serviceClientWrapper";

module.exports = function (nodecg: NodeCG) {
nodecg.log.info("Sample bundle for twitch-pubsub started");

const pubsub = requireService<PubSubServiceClient>(nodecg, "twitch-pubsub");

pubsub?.onAvailable((client) => {
nodecg.log.info("PubSub client has been updated, adding handlers for messages.");
client.onSubscription((message) => {
console.log(`${message.userDisplayName} just subscribed (${message.cumulativeMonths} months)`);
});
client.onBits((message) => {
console.log(`${message.userName} cheered ${message.bits} Bits`);
});
client.onBitsBadgeUnlock((message) => {
console.log(`${message.userName} just unlocked the ${message.badgeTier} Badge`);
});
client.onRedemption((message) => {
console.log(`${message.userDisplayName} redeemed ${message.rewardName} (${message.message})`);
});
});

pubsub?.onUnavailable(() => nodecg.log.info("PubSub client has been unset."));
};
25 changes: 25 additions & 0 deletions samples/pubsubsample/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "pubsubsample",
"version": "0.1.0",
"nodecg": {
"compatibleRange": "^1.1.1",
"bundleDependencies": {
"nodecg-io-twitch-pubsub": "0.1.0"
}
},
"scripts": {
"build": "tsc",
"watch": "tsc -w"
},
"license": "MIT",
"devDependencies": {
"@types/ws": "^7.2.6",
"@types/node": "^14.6.4"
},
"dependencies": {
"nodecg-io-twitch-pubsub": "0.1.0",
"nodecg-io-core": "0.1.0",
"nodecg": "^1.6.1",
"typescript": "^4.0.2"
}
}
3 changes: 3 additions & 0 deletions samples/pubsubsample/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.common.json"
}