Skip to content

Commit

Permalink
Merge pull request #988 from SteffoSpieler/fix/streamelements-doing-a…
Browse files Browse the repository at this point in the history
…pi-changes

StreamElements: Fix API, remove specific Test-Events and add flag for subbomb subscriber event data
  • Loading branch information
hlxid authored Aug 1, 2023
2 parents 7e579c8 + 64c73db commit eaa730f
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 320 deletions.
76 changes: 6 additions & 70 deletions samples/streamelements-events/extension/index.ts
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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 ${
Expand All @@ -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(
Expand All @@ -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
Expand Down
107 changes: 10 additions & 97 deletions services/nodecg-io-streamelements/extension/StreamElements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,16 @@ import {
StreamElementsFollowEvent,
StreamElementsHostEvent,
StreamElementsRaidEvent,
StreamElementsReplayEvent,
StreamElementsSubBombEvent,
StreamElementsSubscriberEvent,
StreamElementsTestCheerEvent,
StreamElementsTestEvent,
StreamElementsTestFollowEvent,
StreamElementsTestHostEvent,
StreamElementsTestRaidEvent,
StreamElementsTestSubscriberEvent,
StreamElementsTestTipEvent,
StreamElementsTipEvent,
} from "./StreamElementsEvent";
import { EventEmitter } from "events";
import NodeCG from "@nodecg/types";

export interface StreamElementsReplicant {
lastSubscriber?: StreamElementsSubscriberEvent;
lastSubBomb?: StreamElementsSubBombEvent<StreamElementsSubscriberEvent>;
lastSubBomb?: StreamElementsSubBombEvent;
lastTip?: StreamElementsTipEvent;
lastCheer?: StreamElementsCheerEvent;
lastGift?: StreamElementsSubscriberEvent;
Expand All @@ -37,14 +29,14 @@ export interface StreamElementsReplicant {
*/
interface SubBomb {
timeout: NodeJS.Timeout;
subs: Array<StreamElementsSubscriberEvent | StreamElementsTestSubscriberEvent>;
subs: Array<StreamElementsSubscriberEvent>;
}

export class StreamElementsServiceClient extends EventEmitter {
private socket: SocketIOClient.Socket;
private subBombDetectionMap: Map<string, SubBomb> = new Map();

constructor(private jwtToken: string, private handleTestEvents: boolean) {
constructor(private jwtToken: string) {
super();
}

Expand Down Expand Up @@ -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<T extends StreamElementsSubscriberEvent | StreamElementsTestSubscriberEvent>(
private handleSubGift<T extends StreamElementsSubscriberEvent>(
subGifter: string | undefined,
gift: T,
handlerSubBomb: (data: StreamElementsSubBombEvent<T>) => void,
handlerSubBomb: (data: StreamElementsSubBombEvent) => void,
handlerGift: (data: T) => void,
) {
const gifter = subGifter ?? "anonymous";
Expand All @@ -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);
Expand Down Expand Up @@ -183,36 +159,14 @@ 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;
handler(data);
});
}

public onSubscriberBomb(handler: (data: StreamElementsSubBombEvent<StreamElementsSubscriberEvent>) => void): void {
public onSubscriberBomb(handler: (data: StreamElementsSubBombEvent) => void): void {
this.on("subbomb", handler);
}

Expand Down Expand Up @@ -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<StreamElementsTestSubscriberEvent>) => 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<StreamElementsReplicant>): void {
if (rep.value === undefined) {
rep.value = {};
Expand Down
Loading

0 comments on commit eaa730f

Please sign in to comment.