From c95cd01fad0b0f2fe1859f4f0c545f3031a1653c Mon Sep 17 00:00:00 2001 From: Daniel Fischer Date: Sun, 20 Jun 2021 21:38:56 +0200 Subject: [PATCH] add EventSub channel.subscription.message --- .eslintrc.js | 2 +- packages/twitch-eventsub/src/EventSubBase.ts | 22 +++ ...EventSubChannelSubscriptionMessageEvent.ts | 141 ++++++++++++++++++ ...bChannelSubscriptionMessageSubscription.ts | 37 +++++ .../API/Helix/EventSub/HelixEventSubApi.ts | 18 +++ 5 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 packages/twitch-eventsub/src/Events/EventSubChannelSubscriptionMessageEvent.ts create mode 100644 packages/twitch-eventsub/src/Subscriptions/EventSubChannelSubscriptionMessageSubscription.ts diff --git a/.eslintrc.js b/.eslintrc.js index 9e91cfe68..2f5c06acd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -63,7 +63,7 @@ const memberNames = [ '^tag_(ids|list)$', '^cooldown_end_time$', '^(last|top)_contributions?$', - '^(streak|cumulative)_months', + '^(streak|cumulative|duration)_months', '^live_only$', '^localization_(names|descriptions)', '^allow_notifications$', diff --git a/packages/twitch-eventsub/src/EventSubBase.ts b/packages/twitch-eventsub/src/EventSubBase.ts index 09b666f5a..e6ecda6ac 100644 --- a/packages/twitch-eventsub/src/EventSubBase.ts +++ b/packages/twitch-eventsub/src/EventSubBase.ts @@ -32,6 +32,7 @@ import type { EventSubChannelRewardEvent } from './Events/EventSubChannelRewardE import type { EventSubChannelSubscriptionEndEvent } from './Events/EventSubChannelSubscriptionEndEvent'; import type { EventSubChannelSubscriptionEvent } from './Events/EventSubChannelSubscriptionEvent'; import type { EventSubChannelSubscriptionGiftEvent } from './Events/EventSubChannelSubscriptionGiftEvent'; +import type { EventSubChannelSubscriptionMessageEvent } from './Events/EventSubChannelSubscriptionMessageEvent'; import type { EventSubChannelUnbanEvent } from './Events/EventSubChannelUnbanEvent'; import type { EventSubChannelUpdateEvent } from './Events/EventSubChannelUpdateEvent'; import type { EventSubExtensionBitsTransactionCreateEvent } from './Events/EventSubExtensionBitsTransactionCreateEvent'; @@ -63,6 +64,7 @@ import { EventSubChannelRewardRemoveSubscription } from './Subscriptions/EventSu import { EventSubChannelRewardUpdateSubscription } from './Subscriptions/EventSubChannelRewardUpdateSubscription'; import { EventSubChannelSubscriptionEndSubscription } from './Subscriptions/EventSubChannelSubscriptionEndSubscription'; import { EventSubChannelSubscriptionGiftSubscription } from './Subscriptions/EventSubChannelSubscriptionGiftSubscription'; +import { EventSubChannelSubscriptionMessageSubscription } from './Subscriptions/EventSubChannelSubscriptionMessageSubscription'; import { EventSubChannelSubscriptionSubscription } from './Subscriptions/EventSubChannelSubscriptionSubscription'; import { EventSubChannelUnbanSubscription } from './Subscriptions/EventSubChannelUnbanSubscription'; import { EventSubChannelUpdateSubscription } from './Subscriptions/EventSubChannelUpdateSubscription'; @@ -253,6 +255,26 @@ export abstract class EventSubBase { return this._genericSubscribe(EventSubChannelSubscriptionGiftSubscription, handler, this, userId); } + /** + * Subscribes to events that represent a user's subscription to a channel being announced. + * + * @param user The user for which to get notifications for about announced subscriptions. + * @param handler The function that will be called for any new notifications. + */ + async subscribeToChannelSubscriptionMessageEvents( + user: UserIdResolvable, + handler: (event: EventSubChannelSubscriptionMessageEvent) => void + ): Promise { + const userId = extractUserId(user); + + if (!numberRegex.test(userId)) { + this._logger.warn( + 'EventSubListener#subscribeToChannelSubscriptionMessageEvents: The given user is a non-numeric string. You might be sending a user name instead of a user ID.' + ); + } + return this._genericSubscribe(EventSubChannelSubscriptionMessageSubscription, handler, this, userId); + } + /** * Subscribes to events that represent a user's subscription to a channel ending. * diff --git a/packages/twitch-eventsub/src/Events/EventSubChannelSubscriptionMessageEvent.ts b/packages/twitch-eventsub/src/Events/EventSubChannelSubscriptionMessageEvent.ts new file mode 100644 index 000000000..2d5c4c7ed --- /dev/null +++ b/packages/twitch-eventsub/src/Events/EventSubChannelSubscriptionMessageEvent.ts @@ -0,0 +1,141 @@ +import { Enumerable } from '@d-fischer/shared-utils'; +import type { ApiClient, HelixUser } from 'twitch'; +import { rtfm } from 'twitch-common'; + +/** + * The tier of a subscription. 1000 means tier 1, and so on. + */ +export type EventSubChannelSubscriptionMessageEventTier = '1000' | '2000' | '3000'; + +/** @private */ +export interface EventSubChannelSubscriptionMessageEmoteData { + begin: number; + end: number; + id: string; +} + +/** @private */ +export interface EventSubChannelSubscriptionMessageData { + text: string; + emotes: EventSubChannelSubscriptionMessageEmoteData[]; +} + +/** @private */ +export interface EventSubChannelSubscriptionMessageEventData { + user_id: string; + user_login: string; + user_name: string; + broadcaster_user_id: string; + broadcaster_user_login: string; + broadcaster_user_name: string; + tier: EventSubChannelSubscriptionMessageEventTier; + message: EventSubChannelSubscriptionMessageData; + cumulative_months: number; + streak_months: number | null; + duration_months: number; +} + +/** + * An EventSub event representing the public announcement of a channel subscription by the subscriber. + */ +@rtfm('twitch-eventsub', 'EventSubChannelSubscriptionMessageEvent', 'userId') +export class EventSubChannelSubscriptionMessageEvent { + /** @private */ + @Enumerable(false) protected readonly _client: ApiClient; + + /** @private */ + constructor(private readonly _data: EventSubChannelSubscriptionMessageEventData, client: ApiClient) { + this._client = client; + } + + /** + * The ID of the user whose subscription is being announced. + */ + get userId(): string { + return this._data.user_id; + } + + /** + * The name of the user whose subscription is being announced. + */ + get userName(): string { + return this._data.user_login; + } + + /** + * The display name of the user whose subscription is being announced. + */ + get userDisplayName(): string { + return this._data.user_name; + } + + /** + * Retrieves more information about the user whose subscription is being announced. + */ + async getUser(): Promise { + return (await this._client.helix.users.getUserById(this._data.user_id))!; + } + + /** + * The ID of the broadcaster. + */ + get broadcasterId(): string { + return this._data.broadcaster_user_id; + } + + /** + * The name of the broadcaster. + */ + get broadcasterName(): string { + return this._data.broadcaster_user_login; + } + + /** + * The display name of the broadcaster. + */ + get broadcasterDisplayName(): string { + return this._data.broadcaster_user_name; + } + + /** + * Retrieves more information about the broadcaster. + */ + async getBroadcaster(): Promise { + return (await this._client.helix.users.getUserById(this._data.broadcaster_user_id))!; + } + + /** + * The tier of the subscription, either 1000, 2000 or 3000. + */ + get tier(): EventSubChannelSubscriptionMessageEventTier { + return this._data.tier; + } + + /** + * The total number of months the user has been subscribed. + */ + get cumulativeMonths(): number { + return this._data.cumulative_months; + } + + /** + * The number of months the user has been subscribed in a row, or null if they don't want to share it. + */ + get streakMonths(): number | null { + return this._data.streak_months; + } + + /** + * The number of months the user has now subscribed. + */ + get durationMonths(): number { + return this._data.duration_months; + } + + /** + * The text of the message. + */ + get messageText(): string { + return this._data.message.text; + } +} diff --git a/packages/twitch-eventsub/src/Subscriptions/EventSubChannelSubscriptionMessageSubscription.ts b/packages/twitch-eventsub/src/Subscriptions/EventSubChannelSubscriptionMessageSubscription.ts new file mode 100644 index 000000000..25ed3362a --- /dev/null +++ b/packages/twitch-eventsub/src/Subscriptions/EventSubChannelSubscriptionMessageSubscription.ts @@ -0,0 +1,37 @@ +import type { HelixEventSubSubscription } from 'twitch'; +import { rtfm } from 'twitch-common'; +import type { EventSubChannelSubscriptionMessageEventData } from '../Events/EventSubChannelSubscriptionMessageEvent'; +import { EventSubChannelSubscriptionMessageEvent } from '../Events/EventSubChannelSubscriptionMessageEvent'; +import type { EventSubBase } from '../EventSubBase'; +import { EventSubSubscription } from './EventSubSubscription'; + +/** + * @private + */ +@rtfm('twitch-eventsub', 'EventSubSubscription') +export class EventSubChannelSubscriptionMessageSubscription extends EventSubSubscription { + constructor( + handler: (data: EventSubChannelSubscriptionMessageEvent) => void, + client: EventSubBase, + private readonly _userId: string + ) { + super(handler, client); + } + + get id(): string { + return `channel.subscription.end.${this._userId}`; + } + + protected transformData( + data: EventSubChannelSubscriptionMessageEventData + ): EventSubChannelSubscriptionMessageEvent { + return new EventSubChannelSubscriptionMessageEvent(data, this._client._apiClient); + } + + protected async _subscribe(): Promise { + return this._client._apiClient.helix.eventSub.subscribeToChannelSubscriptionMessageEvents( + this._userId, + await this._getTransportOptions() + ); + } +} diff --git a/packages/twitch/src/API/Helix/EventSub/HelixEventSubApi.ts b/packages/twitch/src/API/Helix/EventSub/HelixEventSubApi.ts index ea674c0d1..cee87d9f2 100644 --- a/packages/twitch/src/API/Helix/EventSub/HelixEventSubApi.ts +++ b/packages/twitch/src/API/Helix/EventSub/HelixEventSubApi.ts @@ -348,6 +348,24 @@ export class HelixEventSubApi extends BaseApi { ); } + /** + * Subscribe to events that represent a user's subscription to a channel being announced. + * + * @param broadcaster The broadcaster you want to listen to subscription message events for. + * @param transport The transport options + */ + async subscribeToChannelSubscriptionMessageEvents( + broadcaster: UserIdResolvable, + transport: HelixEventSubTransportOptions + ): Promise { + return this.createSubscription( + 'channel.subscription.message', + '1', + { broadcaster_user_id: extractUserId(broadcaster) }, + transport + ); + } + /** * Subscribe to events that represent a user's subscription to a channel ending. *