diff --git a/samples/streamelements-events/extension/index.ts b/samples/streamelements-events/extension/index.ts index 33f0e7840..09d4315a7 100644 --- a/samples/streamelements-events/extension/index.ts +++ b/samples/streamelements-events/extension/index.ts @@ -1,6 +1,6 @@ import NodeCG from "@nodecg/types"; import { StreamElementsReplicant, StreamElementsServiceClient } from "nodecg-io-streamelements"; -import { StreamElementsEvent, StreamElementsTestEvent } from "nodecg-io-streamelements/extension/StreamElementsEvent"; +import { StreamElementsEvent } from "nodecg-io-streamelements/extension/StreamElementsEvent"; import { requireService } from "nodecg-io-core"; module.exports = function (nodecg: NodeCG.ServerAPI) { @@ -16,40 +16,20 @@ module.exports = function (nodecg: NodeCG.ServerAPI) { nodecg.log.info(`${getName(data)} just cheered ${data.data.amount} bit(s). Message: ${data.data.message}`); }); - client.onTestCheer((data) => { - nodecg.log.info( - `${getName(data)} just cheered ${data.event.amount} bit(s). Message: ${data.event.message}`, - ); - }); - client.onFollow((data) => { nodecg.log.info(`${getName(data)} just followed.`); }); - client.onTestFollow((data) => { - nodecg.log.info(`${getName(data)} just followed.`); - }); - client.onSubscriber((data) => { nodecg.log.info( `${getName(data)} just subscribed for ${data.data.amount} months (${formatSubTier(data.data.tier)}).`, ); - }); - - client.onTestSubscriber((data) => { - nodecg.log.info( - `${getName(data)} just subscribed for ${data.event.amount} months (${formatSubTier(data.event.tier)}).`, - ); - }); + }, false); client.onSubscriberBomb((data) => { nodecg.log.info(`${data.gifterUsername} just gifted ${data.subscribers.length} subs.`); }); - client.onTestSubscriberBomb((data) => { - nodecg.log.info(`${data.gifterUsername} just gifted ${data.subscribers.length} subs.`); - }); - client.onGift((data) => { nodecg.log.info( `${getName(data)} just got a tier ${formatSubTier(data.data.tier)} subscription from ${ @@ -58,30 +38,14 @@ module.exports = function (nodecg: NodeCG.ServerAPI) { ); }); - client.onTestGift((data) => { - nodecg.log.info( - `${getName(data)} just got a tier ${formatSubTier(data.event.tier)} subscription from ${ - data.event.sender ?? "anonymous" - }! It's ${data.event.displayName}'s ${data.event.amount} month.`, - ); - }); - client.onHost((data) => { nodecg.log.info(`${getName(data)} just hosted the stream for ${data.data.amount} viewer(s).`); }); - client.onTestHost((data) => { - nodecg.log.info(`${getName(data)} just hosted the stream for ${data.event.amount} viewer(s).`); - }); - client.onRaid((data) => { nodecg.log.info(`${getName(data)} just raided the stream with ${data.data.amount} viewers.`); }); - client.onTestRaid((data) => { - nodecg.log.info(`${getName(data)} just raided the stream with ${data.event.amount} viewers.`); - }); - client.onTip((data) => { if (data.data.currency) { nodecg.log.info( @@ -92,46 +56,18 @@ module.exports = function (nodecg: NodeCG.ServerAPI) { } }); - client.onTestTip((data) => { - nodecg.log.info( - `${getName(data)} just donated ${data.event.amount} ${data.event.currency}. Message. ${ - data.event.message - }`, - ); - }); - - client.onTest((data) => { - nodecg.log.info(JSON.stringify(data)); - }); - client.setupReplicant(streamElementsReplicant); }); streamElements?.onUnavailable(() => nodecg.log.info("SE client has been unset.")); }; -/** - * Gets the name of the user from the event. - * Defaults to use the displayName that is sent by StreamElements. - * The displayName contains capitalization and special characters. - * If the displayName is not available, the normal username of the relevant platform is used. - * Supports real and test events. - */ -function getName(event: StreamElementsEvent | StreamElementsTestEvent): string { - const eventData = "data" in event ? event.data : event.event; - - if ("displayName" in eventData && typeof eventData.displayName === "string") { - return eventData.displayName; - } else if ("name" in eventData && typeof eventData.name === "string") { - return eventData.name; - } else if ("username" in eventData && typeof eventData.username === "string") { - return eventData.username; - } else { - return "unknown"; - } +function getName(event: StreamElementsEvent): string { + return event.data.displayName ?? event.data.username; } -function formatSubTier(tier: "1000" | "2000" | "3000" | "prime"): string { +function formatSubTier(tier?: "1000" | "2000" | "3000" | "prime"): string { + if (!tier) return "unknown"; if (tier === "prime") return "Twitch Prime"; // We want to display the tier as 1, 2, 3 diff --git a/services/nodecg-io-streamelements/extension/StreamElements.ts b/services/nodecg-io-streamelements/extension/StreamElements.ts index e5ac95242..d60233b49 100644 --- a/services/nodecg-io-streamelements/extension/StreamElements.ts +++ b/services/nodecg-io-streamelements/extension/StreamElements.ts @@ -6,16 +6,8 @@ import { StreamElementsFollowEvent, StreamElementsHostEvent, StreamElementsRaidEvent, - StreamElementsReplayEvent, StreamElementsSubBombEvent, StreamElementsSubscriberEvent, - StreamElementsTestCheerEvent, - StreamElementsTestEvent, - StreamElementsTestFollowEvent, - StreamElementsTestHostEvent, - StreamElementsTestRaidEvent, - StreamElementsTestSubscriberEvent, - StreamElementsTestTipEvent, StreamElementsTipEvent, } from "./StreamElementsEvent"; import { EventEmitter } from "events"; @@ -23,7 +15,7 @@ import NodeCG from "@nodecg/types"; export interface StreamElementsReplicant { lastSubscriber?: StreamElementsSubscriberEvent; - lastSubBomb?: StreamElementsSubBombEvent; + lastSubBomb?: StreamElementsSubBombEvent; lastTip?: StreamElementsTipEvent; lastCheer?: StreamElementsCheerEvent; lastGift?: StreamElementsSubscriberEvent; @@ -37,14 +29,14 @@ export interface StreamElementsReplicant { */ interface SubBomb { timeout: NodeJS.Timeout; - subs: Array; + subs: Array; } export class StreamElementsServiceClient extends EventEmitter { private socket: SocketIOClient.Socket; private subBombDetectionMap: Map = new Map(); - constructor(private jwtToken: string, private handleTestEvents: boolean) { + constructor(private jwtToken: string) { super(); } @@ -73,32 +65,12 @@ export class StreamElementsServiceClient extends EventEmitter { } this.emit(data.type, data); }); - - if (this.handleTestEvents) { - this.onTestEvent((data: StreamElementsTestEvent) => { - if (data.listener) { - this.emit("test", data); - this.emit("test:" + data.listener, data); - } - }); - - this.onTestSubscriber((data) => { - if (data.event.gifted) { - this.handleSubGift( - data.event.sender, - data, - (subBomb) => this.emit("test:subbomb", subBomb), - (gift) => this.emit("test:gift", gift), - ); - } - }); - } } - private handleSubGift( + private handleSubGift( subGifter: string | undefined, gift: T, - handlerSubBomb: (data: StreamElementsSubBombEvent) => void, + handlerSubBomb: (data: StreamElementsSubBombEvent) => void, handlerGift: (data: T) => void, ) { const gifter = subGifter ?? "anonymous"; @@ -116,6 +88,10 @@ export class StreamElementsServiceClient extends EventEmitter { subscribers: subBomb.subs as T[], }; handlerSubBomb(subBombEvent); + + subBomb.subs.forEach(sub => { + sub.data.isFromSubBomb = true; + }); } subBomb.subs.forEach(handlerGift); @@ -183,28 +159,6 @@ export class StreamElementsServiceClient extends EventEmitter { }); } - private onTestEvent(handler: (data: StreamElementsTestEvent) => void): void { - this.socket.on("event:test", (data: StreamElementsTestEvent) => { - if (data) { - handler(data); - } - }); - - this.socket.on("event:update", (data: StreamElementsReplayEvent) => { - // event:update is all replays of previous real events. - // Because the structure is similar to the test events and just the keys in the root element - // are named differently we rename those to align with the naming in the test events - // and handle it as a test event from here on. - if (data) { - handler({ - event: data.data, - listener: data.name, - provider: data.provider, - } as unknown as StreamElementsTestEvent); - } - }); - } - public onSubscriber(handler: (data: StreamElementsSubscriberEvent) => void, includeSubGifts = true): void { this.on("subscriber", (data) => { if (data.data.gifted && !includeSubGifts) return; @@ -212,7 +166,7 @@ export class StreamElementsServiceClient extends EventEmitter { }); } - public onSubscriberBomb(handler: (data: StreamElementsSubBombEvent) => void): void { + public onSubscriberBomb(handler: (data: StreamElementsSubBombEvent) => void): void { this.on("subbomb", handler); } @@ -240,47 +194,6 @@ export class StreamElementsServiceClient extends EventEmitter { this.on("host", handler); } - public onTest(handler: (data: StreamElementsEvent) => void): void { - this.on("test", handler); - } - - public onTestSubscriber(handler: (data: StreamElementsTestSubscriberEvent) => void, includeSubGifts = true): void { - this.on("test:subscriber-latest", (data) => { - if (data.event.gifted && !includeSubGifts) return; - handler(data); - }); - } - - public onTestSubscriberBomb( - handler: (data: StreamElementsSubBombEvent) => void, - ): void { - this.on("test:subbomb", handler); - } - - public onTestGift(handler: (data: StreamElementsTestSubscriberEvent) => void): void { - this.on("test:gift", handler); - } - - public onTestCheer(handler: (data: StreamElementsTestCheerEvent) => void): void { - this.on("test:cheer-latest", handler); - } - - public onTestFollow(handler: (data: StreamElementsTestFollowEvent) => void): void { - this.on("test:follower-latest", handler); - } - - public onTestRaid(handler: (data: StreamElementsTestRaidEvent) => void): void { - this.on("test:raid-latest", handler); - } - - public onTestHost(handler: (data: StreamElementsTestHostEvent) => void): void { - this.on("test:host-latest", handler); - } - - public onTestTip(handler: (data: StreamElementsTestTipEvent) => void): void { - this.on("test:tip-latest", handler); - } - public setupReplicant(rep: NodeCG.ServerReplicant): void { if (rep.value === undefined) { rep.value = {}; diff --git a/services/nodecg-io-streamelements/extension/StreamElementsEvent.ts b/services/nodecg-io-streamelements/extension/StreamElementsEvent.ts index fd2f63b74..cd8c15775 100644 --- a/services/nodecg-io-streamelements/extension/StreamElementsEvent.ts +++ b/services/nodecg-io-streamelements/extension/StreamElementsEvent.ts @@ -1,5 +1,3 @@ -import { ObjectMap } from "nodecg-io-core"; - interface StreamElementsBaseEvent { /** * StreamElements hexadecimal Event ID @@ -25,7 +23,11 @@ interface StreamElementsBaseEvent { * The internal event data. */ data: TData & StreamElementsDataBase; - flagged: boolean; + flagged?: boolean; + /** + * Whether the event was replayed or emulated. Is undefined when the event is a real live event. + */ + isMock?: boolean; /** * Event provider */ @@ -42,9 +44,9 @@ interface StreamElementsDataBase { */ avatar: string; /** - * The users display name. + * The users display name. Never set for mocked events. */ - displayName: string; + displayName?: string; /** * The user's Twitch username. */ @@ -102,6 +104,10 @@ export type StreamElementsSubscriberEvent = StreamElementsBaseEvent< * True if this sub was gifted by someone else. */ gifted?: boolean; + /** + * True if this sub is part of a subbomb. If not, it is undefined. + */ + isFromSubBomb?: true; /** * The username of the user that has gifted this sub. */ @@ -117,13 +123,11 @@ export type StreamElementsSubscriberEvent = StreamElementsBaseEvent< /** * The tier of the subscription. */ - tier: "1000" | "2000" | "3000" | "prime"; + tier?: "1000" | "2000" | "3000" | "prime"; } >; -export interface StreamElementsSubBombEvent< - T extends StreamElementsSubscriberEvent | StreamElementsTestSubscriberEvent, -> { +export interface StreamElementsSubBombEvent { /** * The username of the gifter. */ @@ -131,7 +135,7 @@ export interface StreamElementsSubBombEvent< /** * All gifted subs. */ - subscribers: ReadonlyArray; + subscribers: ReadonlyArray; } export type StreamElementsTipEvent = StreamElementsBaseEvent< @@ -156,122 +160,6 @@ export type StreamElementsTipEvent = StreamElementsBaseEvent< } >; -interface StreamElementsBaseTestEvent { - /** - * Event provider - */ - provider?: "twitch" | "youtube" | "facebook"; - listener: TListener; - event: TEvent & StreamElementsTestDataBase; -} - -interface StreamElementsTestDataBase { - /** - * The url of the user's avatar. - */ - avatar: string; - /** - * The users display name. - */ - displayName: string; - /** - * The user's Twitch username. - */ - name: string; - /** - * The Twitch Channel ID - */ - providerId?: string; -} - -export type StreamElementsTestFollowEvent = StreamElementsBaseTestEvent<"follower-latest", unknown>; - -export type StreamElementsTestCheerEvent = StreamElementsBaseTestEvent< - "cheer-latest", - { - /** - * The count of bits that were cheered. - */ - amount: number; - /** - * The message contained in the cheer. - */ - message: string; - } ->; - -export type StreamElementsTestHostEvent = StreamElementsBaseTestEvent< - "host-latest", - { - /** - * Number of viewers that are watching through this host. - */ - amount: number; - } ->; - -export type StreamElementsTestRaidEvent = StreamElementsBaseTestEvent< - "raid-latest", - { - /** - * Number of viewers raiding this channel. - */ - amount: number; - } ->; - -export type StreamElementsTestSubscriberEvent = StreamElementsBaseTestEvent< - "subscriber-latest", - { - /** - * The total amount of months that this user has already subscribed. - */ - amount: number; - /** - * True if this sub was gifted by someone else. - */ - gifted?: boolean; - /** - * The username of the user that has gifted this sub. - */ - sender?: string; - /** - * Subscription message by user - */ - message: string; - /** - * Amount of consequent months this user already has subscribed. - */ - streak: number; - /** - * The tier of the subscription. - */ - tier: "1000" | "2000" | "3000" | "prime"; - } ->; - -export type StreamElementsTestTipEvent = StreamElementsBaseTestEvent< - "tip-latest", - { - /** - * The amount of money in the given currency that was tipped. - */ - amount: number; - /** - * The user provided message for this tip. - */ - message: string; - /** - * The currency symbol. - */ - currency: string; - /** - * StreamElements's hexadecimal tip ID. - */ - tipId: string; - } ->; - export type StreamElementsEvent = | StreamElementsFollowEvent | StreamElementsCheerEvent @@ -279,23 +167,3 @@ export type StreamElementsEvent = | StreamElementsRaidEvent | StreamElementsSubscriberEvent | StreamElementsTipEvent; - -export type StreamElementsTestEvent = - | StreamElementsTestFollowEvent - | StreamElementsTestCheerEvent - | StreamElementsTestHostEvent - | StreamElementsTestRaidEvent - | StreamElementsTestSubscriberEvent - | StreamElementsTestTipEvent; - -/** - * When replaying real events the structure is similar to the test events - * except for the keys in the root object. - * This is a replay event general for all types. - * The data structure and name follows the same schema as the test events. - */ -export interface StreamElementsReplayEvent { - provider?: "twitch" | "youtube" | "facebook"; - name: string; - data: ObjectMap; -} diff --git a/services/nodecg-io-streamelements/extension/index.ts b/services/nodecg-io-streamelements/extension/index.ts index 1fa75b5ee..81bf82561 100644 --- a/services/nodecg-io-streamelements/extension/index.ts +++ b/services/nodecg-io-streamelements/extension/index.ts @@ -16,12 +16,12 @@ module.exports = (nodecg: NodeCG.ServerAPI) => { class StreamElementsService extends ServiceBundle { async validateConfig(config: StreamElementsServiceConfig) { - return new StreamElementsServiceClient(config.jwtToken, config.handleTestEvents).testConnection(); + return new StreamElementsServiceClient(config.jwtToken).testConnection(); } async createClient(config: StreamElementsServiceConfig, logger: Logger) { logger.info("Connecting to StreamElements socket server..."); - const client = new StreamElementsServiceClient(config.jwtToken, config.handleTestEvents); + const client = new StreamElementsServiceClient(config.jwtToken); await client.connect(); logger.info("Successfully connected to StreamElements socket server."); diff --git a/services/nodecg-io-streamelements/streamelements-schema.json b/services/nodecg-io-streamelements/streamelements-schema.json index da56ed330..f971af224 100644 --- a/services/nodecg-io-streamelements/streamelements-schema.json +++ b/services/nodecg-io-streamelements/streamelements-schema.json @@ -6,11 +6,6 @@ "jwtToken": { "type": "string", "description": "Your JWT token for streamelments." - }, - "handleTestEvents": { - "type": "boolean", - "default": false, - "description": "Whether test events should be handled." } }, "required": ["jwtToken"]