Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.

Commit 13edf27

Browse files
authored
Merge pull request #146 from derNiklaas/pubsub-client
Added PubSub package and sample
2 parents 842cd3f + fa62cae commit 13edf27

File tree

8 files changed

+233
-0
lines changed

8 files changed

+233
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { NodeCG } from "nodecg/types/server";
2+
import { emptySuccess, Result, success } from "nodecg-io-core/extension/utils/result";
3+
import { ServiceBundle } from "nodecg-io-core/extension/serviceBundle";
4+
import { PubSubServiceClient } from "./pubSubClient";
5+
6+
export { PubSubServiceClient } from "./pubSubClient";
7+
8+
export interface PubSubServiceConfig {
9+
oauthKey: string;
10+
}
11+
12+
module.exports = (nodecg: NodeCG) => {
13+
new TwitchPubSubService(nodecg, "twitch-pubsub", __dirname, "../pubsub-schema.json").register();
14+
};
15+
16+
class TwitchPubSubService extends ServiceBundle<PubSubServiceConfig, PubSubServiceClient> {
17+
async validateConfig(config: PubSubServiceConfig): Promise<Result<void>> {
18+
await PubSubServiceClient.getTokenInfo(config); // This will throw a error if the token is invalid
19+
return emptySuccess();
20+
}
21+
22+
async createClient(config: PubSubServiceConfig): Promise<Result<PubSubServiceClient>> {
23+
const client = await PubSubServiceClient.createClient(config);
24+
25+
return success(client);
26+
}
27+
28+
stopClient(_: PubSubServiceClient): void {
29+
// Not possible
30+
}
31+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { ServiceClient } from "nodecg-io-core/extension/types";
2+
import { getTokenInfo, StaticAuthProvider, TokenInfo } from "twitch-auth";
3+
import { PubSubServiceConfig } from "./index";
4+
import {
5+
PubSubBitsBadgeUnlockMessage,
6+
PubSubBitsMessage,
7+
PubSubChatModActionMessage,
8+
PubSubClient,
9+
PubSubListener,
10+
PubSubRedemptionMessage,
11+
PubSubSubscriptionMessage,
12+
PubSubWhisperMessage,
13+
} from "twitch-pubsub-client";
14+
import { ApiClient } from "twitch";
15+
16+
export class PubSubServiceClient implements ServiceClient<PubSubClient> {
17+
constructor(private client: PubSubClient, private userid: string) {}
18+
19+
/**
20+
* Creates a instance of TwitchServiceClient using the credentials from the passed config.
21+
*/
22+
static async createClient(cfg: PubSubServiceConfig): Promise<PubSubServiceClient> {
23+
// Create a twitch authentication provider
24+
const tokenInfo = await PubSubServiceClient.getTokenInfo(cfg);
25+
const authProvider = new StaticAuthProvider(tokenInfo.clientId, this.normalizeToken(cfg), tokenInfo.scopes);
26+
27+
// Create the actual chat client and connect
28+
const apiClient = new ApiClient({ authProvider });
29+
const pubSubClient = new PubSubClient();
30+
const userID = await pubSubClient.registerUserListener(apiClient);
31+
32+
return new PubSubServiceClient(pubSubClient, userID);
33+
}
34+
35+
/**
36+
* Gets the token info for the passed config.
37+
*/
38+
static async getTokenInfo(cfg: PubSubServiceConfig): Promise<TokenInfo> {
39+
return await getTokenInfo(this.normalizeToken(cfg));
40+
}
41+
42+
/**
43+
* Strips any "oauth:" before the token away, because the client needs the token without it.
44+
*/
45+
static normalizeToken(cfg: PubSubServiceConfig): string {
46+
return cfg.oauthKey.replace("oauth:", "");
47+
}
48+
49+
getNativeClient(): PubSubClient {
50+
return this.client;
51+
}
52+
53+
getUserID(): string {
54+
return this.userid;
55+
}
56+
57+
async onRedemption(
58+
callback: (message: PubSubRedemptionMessage) => void,
59+
): Promise<PubSubListener<PubSubRedemptionMessage>> {
60+
return await this.client.onRedemption(this.userid, callback);
61+
}
62+
63+
async onSubscription(
64+
callback: (message: PubSubSubscriptionMessage) => void,
65+
): Promise<PubSubListener<PubSubSubscriptionMessage>> {
66+
return await this.client.onSubscription(this.userid, callback);
67+
}
68+
69+
async onBits(callback: (message: PubSubBitsMessage) => void): Promise<PubSubListener<PubSubBitsMessage>> {
70+
return await this.client.onBits(this.userid, callback);
71+
}
72+
73+
async onBitsBadgeUnlock(
74+
callback: (message: PubSubBitsBadgeUnlockMessage) => void,
75+
): Promise<PubSubListener<PubSubBitsBadgeUnlockMessage>> {
76+
return await this.client.onBitsBadgeUnlock(this.userid, callback);
77+
}
78+
79+
async onModAction(
80+
channelID: string,
81+
callback: (message: PubSubChatModActionMessage) => void,
82+
): Promise<PubSubListener<PubSubChatModActionMessage>> {
83+
return await this.client.onModAction(this.userid, channelID, callback);
84+
}
85+
86+
async onWhisper(callback: (message: PubSubWhisperMessage) => void): Promise<PubSubListener<PubSubWhisperMessage>> {
87+
return await this.client.onWhisper(this.userid, callback);
88+
}
89+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "nodecg-io-twitch-pubsub",
3+
"version": "0.1.0",
4+
"description": "Allows access to the Twitch PubSub API.",
5+
"homepage": "https://nodecg.io/samples/twitch-pubsub",
6+
"author": {
7+
"name": "derNiklaas",
8+
"url": "https://github.com/derNiklaas"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "https://github.com/codeoverflow-org/nodecg-io.git",
13+
"directory": "nodecg-io-twitch-pubsub"
14+
},
15+
"main": "extension",
16+
"scripts": {
17+
"build": "tsc -b",
18+
"watch": "tsc -b -w",
19+
"clean": "tsc -b --clean"
20+
},
21+
"keywords": [
22+
"nodecg-io",
23+
"nodecg-bundle"
24+
],
25+
"nodecg": {
26+
"compatibleRange": "^1.1.1",
27+
"bundleDependencies": {
28+
"nodecg-io-core": "^0.1.0"
29+
}
30+
},
31+
"license": "MIT",
32+
"devDependencies": {
33+
"@types/node": "^14.6.4",
34+
"nodecg": "^1.6.1",
35+
"typescript": "^4.0.2"
36+
},
37+
"dependencies": {
38+
"nodecg-io-core": "^0.1.0",
39+
"twitch": "^4.3.6",
40+
"twitch-pubsub-client": "^4.3.6",
41+
"ws": "^7.4.1"
42+
}
43+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"type": "object",
4+
"additionalProperties": false,
5+
"properties": {
6+
"oauthKey": {
7+
"type": "string",
8+
"description": "A OAuth2 key for the twitch api."
9+
}
10+
},
11+
"required": ["oauthKey"]
12+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "../tsconfig.common.json"
3+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { NodeCG } from "nodecg/types/server";
2+
import { PubSubServiceClient } from "nodecg-io-twitch-pubsub/extension";
3+
import { requireService } from "nodecg-io-core/extension/serviceClientWrapper";
4+
5+
module.exports = function (nodecg: NodeCG) {
6+
nodecg.log.info("Sample bundle for twitch-pubsub started");
7+
8+
const pubsub = requireService<PubSubServiceClient>(nodecg, "twitch-pubsub");
9+
10+
pubsub?.onAvailable((client) => {
11+
nodecg.log.info("PubSub client has been updated, adding handlers for messages.");
12+
client.onSubscription((message) => {
13+
console.log(`${message.userDisplayName} just subscribed (${message.cumulativeMonths} months)`);
14+
});
15+
client.onBits((message) => {
16+
console.log(`${message.userName} cheered ${message.bits} Bits`);
17+
});
18+
client.onBitsBadgeUnlock((message) => {
19+
console.log(`${message.userName} just unlocked the ${message.badgeTier} Badge`);
20+
});
21+
client.onRedemption((message) => {
22+
console.log(`${message.userDisplayName} redeemed ${message.rewardName} (${message.message})`);
23+
});
24+
});
25+
26+
pubsub?.onUnavailable(() => nodecg.log.info("PubSub client has been unset."));
27+
};

samples/pubsubsample/package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "pubsubsample",
3+
"version": "0.1.0",
4+
"nodecg": {
5+
"compatibleRange": "^1.1.1",
6+
"bundleDependencies": {
7+
"nodecg-io-twitch-pubsub": "0.1.0"
8+
}
9+
},
10+
"scripts": {
11+
"build": "tsc",
12+
"watch": "tsc -w"
13+
},
14+
"license": "MIT",
15+
"devDependencies": {
16+
"@types/ws": "^7.2.6",
17+
"@types/node": "^14.6.4"
18+
},
19+
"dependencies": {
20+
"nodecg-io-twitch-pubsub": "0.1.0",
21+
"nodecg-io-core": "0.1.0",
22+
"nodecg": "^1.6.1",
23+
"typescript": "^4.0.2"
24+
}
25+
}

samples/pubsubsample/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "../../tsconfig.common.json"
3+
}

0 commit comments

Comments
 (0)