diff --git a/.eslintrc.js b/.eslintrc.js index 1d019da3c..58e09ee52 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,7 +25,7 @@ const memberNames = [ '^click_action$', '^(image_)?url_\\dx$', '^emoticon_sets?$', - '^is_(?:anonymous|broadcast|gift|user_input_required|sub_only|mature|enabled|paused|in_stock|previewable|playlist|(?:verified|known)_bot|live|auto|permanent|recurring|vacation_enabled|canceled)$', + '^is_(?:anonymous|broadcast|gift|user_input_required|sub_only|mature|enabled|paused|in_stock|previewable|playlist|(?:verified|known)_bot|live|auto|permanent|recurring|vacation_enabled|canceled|achieved)$', '^minimum_allowed_role$', '^(chatter|view(er)?)_count$', '^min_bits$', @@ -91,6 +91,7 @@ const memberNames = [ '^viewer_summary$', '^aspect_(?:width|height|ratio_[xy])', '^(?:scale|zoom)_pixels', + '^(?:current|target)_amount$', // HTTP '^Accept$' ]; diff --git a/packages/api/src/ApiClient.ts b/packages/api/src/ApiClient.ts index 62df6f190..696773978 100644 --- a/packages/api/src/ApiClient.ts +++ b/packages/api/src/ApiClient.ts @@ -16,6 +16,7 @@ import { HelixClipApi } from './api/helix/clip/HelixClipApi'; import { HelixEventSubApi } from './api/helix/eventSub/HelixEventSubApi'; import { HelixExtensionsApi } from './api/helix/extensions/HelixExtensionsApi'; import { HelixGameApi } from './api/helix/game/HelixGameApi'; +import { HelixGoalApi } from './api/helix/goals/HelixGoalApi'; import { HelixApiGroup } from './api/helix/HelixApiGroup'; import { HelixRateLimiter } from './api/helix/HelixRateLimiter'; import { HelixHypeTrainApi } from './api/helix/hypeTrain/HelixHypeTrainApi'; @@ -231,6 +232,14 @@ export class ApiClient { return new HelixHypeTrainApi(this); } + /** + * The Helix goal API methods. + */ + @CachedGetter() + get goals(): HelixGoalApi { + return new HelixGoalApi(this); + } + /** * The Helix moderation API methods. */ diff --git a/packages/api/src/api/helix/HelixApiGroup.ts b/packages/api/src/api/helix/HelixApiGroup.ts index 9c59ac4e6..aef65fd6a 100644 --- a/packages/api/src/api/helix/HelixApiGroup.ts +++ b/packages/api/src/api/helix/HelixApiGroup.ts @@ -10,6 +10,7 @@ import { HelixClipApi } from './clip/HelixClipApi'; import { HelixEventSubApi } from './eventSub/HelixEventSubApi'; import { HelixExtensionsApi } from './extensions/HelixExtensionsApi'; import { HelixGameApi } from './game/HelixGameApi'; +import { HelixGoalApi } from './goals/HelixGoalApi'; import { HelixHypeTrainApi } from './hypeTrain/HelixHypeTrainApi'; import { HelixModerationApi } from './moderation/HelixModerationApi'; import { HelixPollApi } from './poll/HelixPollApi'; @@ -97,6 +98,14 @@ export class HelixApiGroup extends BaseApi { return new HelixGameApi(this._client); } + /** + * The Helix goal API methods. + */ + @CachedGetter() + get goals(): HelixGoalApi { + return new HelixGoalApi(this._client); + } + /** * The Helix Hype Train API methods. */ diff --git a/packages/api/src/api/helix/eventSub/HelixEventSubApi.ts b/packages/api/src/api/helix/eventSub/HelixEventSubApi.ts index 0408fc8f7..41e767925 100644 --- a/packages/api/src/api/helix/eventSub/HelixEventSubApi.ts +++ b/packages/api/src/api/helix/eventSub/HelixEventSubApi.ts @@ -805,6 +805,60 @@ export class HelixEventSubApi extends BaseApi { ); } + /** + * Subscribe to events that represent the beginning of a creator goal event in a channel. + * + * @param broadcaster The broadcaster you want to listen to goal begin events for. + * @param transport The transport options. + */ + async subscribeToChannelGoalBeginEvents( + broadcaster: UserIdResolvable, + transport: HelixEventSubTransportOptions + ): Promise { + return await this.createSubscription( + 'channel.goal.begin', + '1', + { broadcaster_user_id: extractUserId(broadcaster) }, + transport + ); + } + + /** + * Subscribe to events that represent progress towards a creator goal. + * + * @param broadcaster The broadcaster for which you want to listen to goal progress events for. + * @param transport The transport options. + */ + async subscribeToChannelGoalProgressEvents( + broadcaster: UserIdResolvable, + transport: HelixEventSubTransportOptions + ): Promise { + return await this.createSubscription( + 'channel.goal.progress', + '1', + { broadcaster_user_id: extractUserId(broadcaster) }, + transport + ); + } + + /** + * Subscribe to events that represent the end of a creator goal event. + * + * @param broadcaster The broadcaster for which you want to listen to goal end events for. + * @param transport The transport options. + */ + async subscribeToChannelGoalEndEvents( + broadcaster: UserIdResolvable, + transport: HelixEventSubTransportOptions + ): Promise { + return await this.createSubscription( + 'channel.goal.end', + '1', + { broadcaster_user_id: extractUserId(broadcaster) }, + transport + ); + } + /** * Subscribe to events that represent the beginning of a Hype Train event in a channel. * diff --git a/packages/api/src/api/helix/goals/HelixGoal.ts b/packages/api/src/api/helix/goals/HelixGoal.ts new file mode 100644 index 000000000..eba16af93 --- /dev/null +++ b/packages/api/src/api/helix/goals/HelixGoal.ts @@ -0,0 +1,103 @@ +import { Enumerable } from '@d-fischer/shared-utils'; +import { DataObject, rawDataSymbol, rtfm } from '@twurple/common'; +import type { ApiClient } from '../../../ApiClient'; +import type { HelixUser } from '../user/HelixUser'; + +export type HelixGoalType = 'follower' | 'subscription'; + +/** @private */ +export interface HelixGoalData { + id: string; + broadcaster_id: string; + broadcaster_name: string; + broadcaster_login: string; + type: HelixGoalType; + description: string; + current_amount: number; + target_amount: number; + created_at: Date; +} + +/** + * A creator goal. + */ +@rtfm('api', 'HelixGoal', 'id') +export class HelixGoal extends DataObject { + @Enumerable(false) private readonly _client: ApiClient; + + /** @private */ + constructor(data: HelixGoalData, client: ApiClient) { + super(data); + this._client = client; + } + + /** + * The ID of the goal. + */ + get id(): string { + return this[rawDataSymbol].id; + } + + /** + * The ID of the broadcaster the goal belongs to. + */ + get broadcasterId(): string { + return this[rawDataSymbol].broadcaster_id; + } + + /** + * The display name of the broadcaster the goal belongs to. + */ + get broadcasterDisplayName(): string { + return this[rawDataSymbol].broadcaster_name; + } + + /** + * The name of the broadcaster the goal belongs to. + */ + get broadcasterName(): string { + return this[rawDataSymbol].broadcaster_login; + } + + /** + * Retrieves more information about the broadcaster. + */ + async getBroadcaster(): Promise { + return (await this._client.users.getUserById(this[rawDataSymbol].broadcaster_id))!; + } + + /** + * The type of the goal. Can be either "follower" or "subscription". + */ + get type(): HelixGoalType { + return this[rawDataSymbol].type; + } + + /** + * The description of the goal. + */ + get description(): string { + return this[rawDataSymbol].description; + } + + /** + * The current value of the goal. + */ + get currentAmount(): number { + return this[rawDataSymbol].current_amount; + } + + /** + * The target value of the goal. + */ + get targetAmount(): number { + return this[rawDataSymbol].target_amount; + } + + /** + * The date and time when the goal was created. + */ + get creationDate(): Date { + return this[rawDataSymbol].created_at; + } +} diff --git a/packages/api/src/api/helix/goals/HelixGoalApi.ts b/packages/api/src/api/helix/goals/HelixGoalApi.ts new file mode 100644 index 000000000..6f14d704c --- /dev/null +++ b/packages/api/src/api/helix/goals/HelixGoalApi.ts @@ -0,0 +1,31 @@ +import type { UserIdResolvable } from '@twurple/common'; +import { extractUserId, rtfm } from '@twurple/common'; +import { BaseApi } from '../../BaseApi'; +import type { HelixResponse } from '../HelixResponse'; +import type { HelixGoalData } from './HelixGoal'; +import { HelixGoal } from './HelixGoal'; + +/** + * The Helix API methods that deal with creator goals. + * + * Can be accessed using `client.helix.goals` on an {@ApiClient} instance. + * + * ## Example + * ```ts + * const api = new ApiClient(new StaticAuthProvider(clientId, accessToken)); + * const { data: goals } = await api.helix.goals.getGoals('61369223'); + */ +@rtfm('api', 'HelixGoalApi') +export class HelixGoalApi extends BaseApi { + async getGoals(broadcaster: UserIdResolvable): Promise { + const result = await this._client.callApi>({ + type: 'helix', + url: 'goals', + query: { + broadcaster_id: extractUserId(broadcaster) + } + }); + + return result.data.map(data => new HelixGoal(data, this._client)); + } +} diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 90535ff06..aacb0b4f1 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -90,6 +90,10 @@ export type { HelixExtensionTransactionData } from './api/helix/extensions/Helix export { HelixGameApi } from './api/helix/game/HelixGameApi'; export { HelixGame } from './api/helix/game/HelixGame'; +export { HelixGoalApi } from './api/helix/goals/HelixGoalApi'; +export { HelixGoal } from './api/helix/goals/HelixGoal'; +export type { HelixGoalData, HelixGoalType } from './api/helix/goals/HelixGoal'; + export { HelixHypeTrainApi } from './api/helix/hypeTrain/HelixHypeTrainApi'; export { HelixHypeTrainContribution } from './api/helix/hypeTrain/HelixHypeTrainContribution'; export type { HelixHypeTrainContributionType } from './api/helix/hypeTrain/HelixHypeTrainContribution'; diff --git a/packages/eventsub/src/EventSubBase.ts b/packages/eventsub/src/EventSubBase.ts index ee5e20655..cc32a53df 100644 --- a/packages/eventsub/src/EventSubBase.ts +++ b/packages/eventsub/src/EventSubBase.ts @@ -16,6 +16,9 @@ import type { Request, RequestHandler } from 'httpanda'; import type { EventSubChannelBanEvent } from './events/EventSubChannelBanEvent'; import type { EventSubChannelCheerEvent } from './events/EventSubChannelCheerEvent'; import type { EventSubChannelFollowEvent } from './events/EventSubChannelFollowEvent'; +import type { EventSubChannelGoalBeginEvent } from './events/EventSubChannelGoalBeginEvent'; +import type { EventSubChannelGoalEndEvent } from './events/EventSubChannelGoalEndEvent'; +import type { EventSubChannelGoalProgressEvent } from './events/EventSubChannelGoalProgressEvent'; import type { EventSubChannelHypeTrainBeginEvent } from './events/EventSubChannelHypeTrainBeginEvent'; import type { EventSubChannelHypeTrainEndEvent } from './events/EventSubChannelHypeTrainEndEvent'; import type { EventSubChannelHypeTrainProgressEvent } from './events/EventSubChannelHypeTrainProgressEvent'; @@ -45,6 +48,9 @@ import type { EventSubUserUpdateEvent } from './events/EventSubUserUpdateEvent'; import { EventSubChannelBanSubscription } from './subscriptions/EventSubChannelBanSubscription'; import { EventSubChannelCheerSubscription } from './subscriptions/EventSubChannelCheerSubscription'; import { EventSubChannelFollowSubscription } from './subscriptions/EventSubChannelFollowSubscription'; +import { EventSubChannelGoalBeginSubscription } from './subscriptions/EventSubChannelGoalBeginSubscription'; +import { EventSubChannelGoalEndSubscription } from './subscriptions/EventSubChannelGoalEndSubscription'; +import { EventSubChannelGoalProgressSubscription } from './subscriptions/EventSubChannelGoalProgressSubscription'; import { EventSubChannelHypeTrainBeginSubscription } from './subscriptions/EventSubChannelHypeTrainBeginSubscription'; import { EventSubChannelHypeTrainEndSubscription } from './subscriptions/EventSubChannelHypeTrainEndSubscription'; import { EventSubChannelHypeTrainProgressSubscription } from './subscriptions/EventSubChannelHypeTrainProgressSubscription'; @@ -839,6 +845,66 @@ To silence this warning without enabling this check (and thus to keep it off eve return await this._genericSubscribe(EventSubChannelPredictionEndSubscription, handler, this, broadcasterId); } + /** + * Subscribes to events that represent a Goal beginning. + * + * @param user The user for which to get notifications about Goals in their channel. + * @param handler The function that will be called for any new notifications. + */ + async subscribeToChannelGoalBeginEvents( + user: UserIdResolvable, + handler: (data: EventSubChannelGoalBeginEvent) => void + ): Promise { + const userId = extractUserId(user); + + if (!numberRegex.test(userId)) { + this._logger.warn( + 'EventSubListener#subscribeToChannelGoalBeginEvents: The given user is a non-numeric string. You might be sending a user name instead of a user ID.' + ); + } + return await this._genericSubscribe(EventSubChannelGoalBeginSubscription, handler, this, userId); + } + + /** + * Subscribes to events that represent progress in a Goal in a channel. + * + * @param user The user for which to get notifications about Goals in their channel. + * @param handler The function that will be called for any new notifications. + */ + async subscribeToChannelGoalProgressEvents( + user: UserIdResolvable, + handler: (data: EventSubChannelGoalProgressEvent) => void + ): Promise { + const userId = extractUserId(user); + + if (!numberRegex.test(userId)) { + this._logger.warn( + 'EventSubListener#subscribeToChannelGoalProgressEvents: The given user is a non-numeric string. You might be sending a user name instead of a user ID.' + ); + } + return await this._genericSubscribe(EventSubChannelGoalProgressSubscription, handler, this, userId); + } + + /** + * Subscribes to events that represent the end of a Goal in a channel. + * + * @param user The user for which to get notifications about Goals in their channel. + * @param handler The function that will be called for any new notifications. + */ + async subscribeToChannelGoalEndEvents( + user: UserIdResolvable, + handler: (data: EventSubChannelGoalEndEvent) => void + ): Promise { + const userId = extractUserId(user); + + if (!numberRegex.test(userId)) { + this._logger.warn( + 'EventSubListener#subscribeToChannelGoalEndEvents: The given user is a non-numeric string. You might be sending a user name instead of a user ID.' + ); + } + return await this._genericSubscribe(EventSubChannelGoalEndSubscription, handler, this, userId); + } + /** * Subscribes to events that represent a Hype Train beginning. * diff --git a/packages/eventsub/src/events/EventSubChannelGoalBeginEvent.ts b/packages/eventsub/src/events/EventSubChannelGoalBeginEvent.ts new file mode 100644 index 000000000..e5c3fefd2 --- /dev/null +++ b/packages/eventsub/src/events/EventSubChannelGoalBeginEvent.ts @@ -0,0 +1,101 @@ +import { Enumerable } from '@d-fischer/shared-utils'; +import type { ApiClient, HelixUser } from '@twurple/api'; +import { DataObject, rawDataSymbol, rtfm } from '@twurple/common'; +import type { EventSubChannelGoalType } from './common/EventSubChannelGoalType'; + +/** @private */ +export interface EventSubChannelGoalBeginEventData { + id: string; + broadcaster_user_id: string; + broadcaster_user_login: string; + broadcaster_user_name: string; + type: EventSubChannelGoalType; + description: string; + current_amount: number; + target_amount: number; + started_at: Date; +} + +/** + * An EventSub event representing a creator goal starting in a channel. + */ +@rtfm('eventsub', 'EventSubChannelGoalBeginEvent', 'broadcasterId') +export class EventSubChannelGoalBeginEvent extends DataObject { + @Enumerable(false) private readonly _client: ApiClient; + + /** @private */ + constructor(data: EventSubChannelGoalBeginEventData, client: ApiClient) { + super(data); + this._client = client; + } + + /** + * The ID of the goal. + */ + get id(): string { + return this[rawDataSymbol].id; + } + + /** + * The ID of the broadcaster. + */ + get broadcasterId(): string { + return this[rawDataSymbol].broadcaster_user_id; + } + + /** + * The name of the broadcaster. + */ + get broadcasterName(): string { + return this[rawDataSymbol].broadcaster_user_login; + } + + /** + * The display name of the broadcaster. + */ + get broadcasterDisplayName(): string { + return this[rawDataSymbol].broadcaster_user_name; + } + + /** + * Retrieves more information about the broadcaster. + */ + async getBroadcaster(): Promise { + return (await this._client.users.getUserById(this[rawDataSymbol].broadcaster_user_id))!; + } + + /** + * The type of the goal. Can be either "follower" or "subscription". + */ + get type(): EventSubChannelGoalType { + return this[rawDataSymbol].type; + } + + /** + * The description of the goal. + */ + get description(): string { + return this[rawDataSymbol].description; + } + + /** + * The current value of the goal. + */ + get currentAmount(): number { + return this[rawDataSymbol].current_amount; + } + + /** + * The target value of the goal. + */ + get targetAmount(): number { + return this[rawDataSymbol].target_amount; + } + + /** + * The time when the goal started. + */ + get startDate(): Date { + return new Date(this[rawDataSymbol].started_at); + } +} diff --git a/packages/eventsub/src/events/EventSubChannelGoalEndEvent.ts b/packages/eventsub/src/events/EventSubChannelGoalEndEvent.ts new file mode 100644 index 000000000..006fd95e2 --- /dev/null +++ b/packages/eventsub/src/events/EventSubChannelGoalEndEvent.ts @@ -0,0 +1,117 @@ +import { Enumerable } from '@d-fischer/shared-utils'; +import type { ApiClient, HelixUser } from '@twurple/api'; +import { DataObject, rawDataSymbol, rtfm } from '@twurple/common'; +import type { EventSubChannelGoalType } from './common/EventSubChannelGoalType'; + +/** @private */ +export interface EventSubChannelGoalEndEventData { + id: string; + broadcaster_user_id: string; + broadcaster_user_login: string; + broadcaster_user_name: string; + type: EventSubChannelGoalType; + description: string; + is_achieved: boolean; + current_amount: number; + target_amount: number; + started_at: Date; + ended_at: Date; +} + +/** + * An EventSub event representing a creator goal starting in a channel. + */ +@rtfm('eventsub', 'EventSubChannelGoalEndEvent', 'broadcasterId') +export class EventSubChannelGoalEndEvent extends DataObject { + @Enumerable(false) private readonly _client: ApiClient; + + /** @private */ + constructor(data: EventSubChannelGoalEndEventData, client: ApiClient) { + super(data); + this._client = client; + } + + /** + * The ID of the goal. + */ + get id(): string { + return this[rawDataSymbol].id; + } + + /** + * The ID of the broadcaster. + */ + get broadcasterId(): string { + return this[rawDataSymbol].broadcaster_user_id; + } + + /** + * The name of the broadcaster. + */ + get broadcasterName(): string { + return this[rawDataSymbol].broadcaster_user_login; + } + + /** + * The display name of the broadcaster. + */ + get broadcasterDisplayName(): string { + return this[rawDataSymbol].broadcaster_user_name; + } + + /** + * Retrieves more information about the broadcaster. + */ + async getBroadcaster(): Promise { + return (await this._client.users.getUserById(this[rawDataSymbol].broadcaster_user_id))!; + } + + /** + * The type of the goal. Can be either "follower" or "subscription". + */ + get type(): EventSubChannelGoalType { + return this[rawDataSymbol].type; + } + + /** + * The description of the goal. + */ + get description(): string { + return this[rawDataSymbol].description; + } + + /** + * Whether the goal has been achieved or not. + */ + get isAchieved(): boolean { + return this[rawDataSymbol].is_achieved; + } + + /** + * The current value of the goal. + */ + get currentAmount(): number { + return this[rawDataSymbol].current_amount; + } + + /** + * The target value of the goal. + */ + get targetAmount(): number { + return this[rawDataSymbol].target_amount; + } + + /** + * The time when the goal started. + */ + get startDate(): Date { + return new Date(this[rawDataSymbol].started_at); + } + + /** + * The time when the goal ended. + */ + get endDate(): Date { + return new Date(this[rawDataSymbol].ended_at); + } +} diff --git a/packages/eventsub/src/events/EventSubChannelGoalProgressEvent.ts b/packages/eventsub/src/events/EventSubChannelGoalProgressEvent.ts new file mode 100644 index 000000000..a62863ac8 --- /dev/null +++ b/packages/eventsub/src/events/EventSubChannelGoalProgressEvent.ts @@ -0,0 +1,101 @@ +import { Enumerable } from '@d-fischer/shared-utils'; +import type { ApiClient, HelixUser } from '@twurple/api'; +import { DataObject, rawDataSymbol, rtfm } from '@twurple/common'; +import type { EventSubChannelGoalType } from './common/EventSubChannelGoalType'; + +/** @private */ +export interface EventSubChannelGoalProgressEventData { + id: string; + broadcaster_user_id: string; + broadcaster_user_login: string; + broadcaster_user_name: string; + type: EventSubChannelGoalType; + description: string; + current_amount: number; + target_amount: number; + started_at: Date; +} + +/** + * An EventSub event representing a creator goal starting in a channel. + */ +@rtfm('eventsub', 'EventSubChannelGoalProgressEvent', 'broadcasterId') +export class EventSubChannelGoalProgressEvent extends DataObject { + @Enumerable(false) private readonly _client: ApiClient; + + /** @private */ + constructor(data: EventSubChannelGoalProgressEventData, client: ApiClient) { + super(data); + this._client = client; + } + + /** + * The ID of the goal. + */ + get id(): string { + return this[rawDataSymbol].id; + } + + /** + * The ID of the broadcaster. + */ + get broadcasterId(): string { + return this[rawDataSymbol].broadcaster_user_id; + } + + /** + * The name of the broadcaster. + */ + get broadcasterName(): string { + return this[rawDataSymbol].broadcaster_user_login; + } + + /** + * The display name of the broadcaster. + */ + get broadcasterDisplayName(): string { + return this[rawDataSymbol].broadcaster_user_name; + } + + /** + * Retrieves more information about the broadcaster. + */ + async getBroadcaster(): Promise { + return (await this._client.users.getUserById(this[rawDataSymbol].broadcaster_user_id))!; + } + + /** + * The type of the goal. Can be either "follower" or "subscription". + */ + get type(): EventSubChannelGoalType { + return this[rawDataSymbol].type; + } + + /** + * The description of the goal. + */ + get description(): string { + return this[rawDataSymbol].description; + } + + /** + * The current value of the goal. + */ + get currentAmount(): number { + return this[rawDataSymbol].current_amount; + } + + /** + * The target value of the goal. + */ + get targetAmount(): number { + return this[rawDataSymbol].target_amount; + } + + /** + * The time when the goal started. + */ + get startDate(): Date { + return new Date(this[rawDataSymbol].started_at); + } +} diff --git a/packages/eventsub/src/events/common/EventSubChannelGoalType.ts b/packages/eventsub/src/events/common/EventSubChannelGoalType.ts new file mode 100644 index 000000000..360f0eb02 --- /dev/null +++ b/packages/eventsub/src/events/common/EventSubChannelGoalType.ts @@ -0,0 +1 @@ +export type EventSubChannelGoalType = 'follower' | 'subscription'; diff --git a/packages/eventsub/src/index.ts b/packages/eventsub/src/index.ts index 0a9a7d98e..9d6b2aa8d 100644 --- a/packages/eventsub/src/index.ts +++ b/packages/eventsub/src/index.ts @@ -15,6 +15,10 @@ export type { ReverseProxyAdapterConfig } from './adapters/ReverseProxyAdapter'; export { EventSubChannelBanEvent } from './events/EventSubChannelBanEvent'; export { EventSubChannelCheerEvent } from './events/EventSubChannelCheerEvent'; export { EventSubChannelFollowEvent } from './events/EventSubChannelFollowEvent'; +export { EventSubChannelGoalBeginEvent } from './events/EventSubChannelGoalBeginEvent'; +export { EventSubChannelGoalEndEvent } from './events/EventSubChannelGoalEndEvent'; +export { EventSubChannelGoalProgressEvent } from './events/EventSubChannelGoalProgressEvent'; +export type { EventSubChannelGoalType } from './events/common/EventSubChannelGoalType'; export { EventSubChannelHypeTrainBeginEvent } from './events/EventSubChannelHypeTrainBeginEvent'; export { EventSubChannelHypeTrainEndEvent } from './events/EventSubChannelHypeTrainEndEvent'; export { EventSubChannelHypeTrainProgressEvent } from './events/EventSubChannelHypeTrainProgressEvent'; diff --git a/packages/eventsub/src/subscriptions/EventSubChannelGoalBeginSubscription.ts b/packages/eventsub/src/subscriptions/EventSubChannelGoalBeginSubscription.ts new file mode 100644 index 000000000..371a1b4be --- /dev/null +++ b/packages/eventsub/src/subscriptions/EventSubChannelGoalBeginSubscription.ts @@ -0,0 +1,35 @@ +import type { HelixEventSubSubscription } from '@twurple/api'; +import { rtfm } from '@twurple/common'; +import type { EventSubChannelGoalBeginEventData } from '../events/EventSubChannelGoalBeginEvent'; +import { EventSubChannelGoalBeginEvent } from '../events/EventSubChannelGoalBeginEvent'; +import type { EventSubBase } from '../EventSubBase'; +import { EventSubSubscription } from './EventSubSubscription'; + +/** + * @private + */ +@rtfm('eventsub', 'EventSubSubscription') +export class EventSubChannelGoalBeginSubscription extends EventSubSubscription { + constructor( + handler: (data: EventSubChannelGoalBeginEvent) => void, + client: EventSubBase, + private readonly _userId: string + ) { + super(handler, client); + } + + get id(): string { + return `channel.goal.begin.${this._userId}`; + } + + protected transformData(data: EventSubChannelGoalBeginEventData): EventSubChannelGoalBeginEvent { + return new EventSubChannelGoalBeginEvent(data, this._client._apiClient); + } + + protected async _subscribe(): Promise { + return await this._client._apiClient.eventSub.subscribeToChannelGoalBeginEvents( + this._userId, + await this._getTransportOptions() + ); + } +} diff --git a/packages/eventsub/src/subscriptions/EventSubChannelGoalEndSubscription.ts b/packages/eventsub/src/subscriptions/EventSubChannelGoalEndSubscription.ts new file mode 100644 index 000000000..9a1490de4 --- /dev/null +++ b/packages/eventsub/src/subscriptions/EventSubChannelGoalEndSubscription.ts @@ -0,0 +1,35 @@ +import type { HelixEventSubSubscription } from '@twurple/api'; +import { rtfm } from '@twurple/common'; +import type { EventSubChannelGoalEndEventData } from '../events/EventSubChannelGoalEndEvent'; +import { EventSubChannelGoalEndEvent } from '../events/EventSubChannelGoalEndEvent'; +import type { EventSubBase } from '../EventSubBase'; +import { EventSubSubscription } from './EventSubSubscription'; + +/** + * @private + */ +@rtfm('eventsub', 'EventSubSubscription') +export class EventSubChannelGoalEndSubscription extends EventSubSubscription { + constructor( + handler: (data: EventSubChannelGoalEndEvent) => void, + client: EventSubBase, + private readonly _userId: string + ) { + super(handler, client); + } + + get id(): string { + return `channel.goal.end.${this._userId}`; + } + + protected transformData(data: EventSubChannelGoalEndEventData): EventSubChannelGoalEndEvent { + return new EventSubChannelGoalEndEvent(data, this._client._apiClient); + } + + protected async _subscribe(): Promise { + return await this._client._apiClient.eventSub.subscribeToChannelGoalEndEvents( + this._userId, + await this._getTransportOptions() + ); + } +} diff --git a/packages/eventsub/src/subscriptions/EventSubChannelGoalProgressSubscription.ts b/packages/eventsub/src/subscriptions/EventSubChannelGoalProgressSubscription.ts new file mode 100644 index 000000000..46e82203d --- /dev/null +++ b/packages/eventsub/src/subscriptions/EventSubChannelGoalProgressSubscription.ts @@ -0,0 +1,35 @@ +import type { HelixEventSubSubscription } from '@twurple/api'; +import { rtfm } from '@twurple/common'; +import type { EventSubChannelGoalProgressEventData } from '../events/EventSubChannelGoalProgressEvent'; +import { EventSubChannelGoalProgressEvent } from '../events/EventSubChannelGoalProgressEvent'; +import type { EventSubBase } from '../EventSubBase'; +import { EventSubSubscription } from './EventSubSubscription'; + +/** + * @private + */ +@rtfm('eventsub', 'EventSubSubscription') +export class EventSubChannelGoalProgressSubscription extends EventSubSubscription { + constructor( + handler: (data: EventSubChannelGoalProgressEvent) => void, + client: EventSubBase, + private readonly _userId: string + ) { + super(handler, client); + } + + get id(): string { + return `channel.goal.progress.${this._userId}`; + } + + protected transformData(data: EventSubChannelGoalProgressEventData): EventSubChannelGoalProgressEvent { + return new EventSubChannelGoalProgressEvent(data, this._client._apiClient); + } + + protected async _subscribe(): Promise { + return await this._client._apiClient.eventSub.subscribeToChannelGoalProgressEvents( + this._userId, + await this._getTransportOptions() + ); + } +}