@overlaysymphony/twitch 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/package.json +40 -0
- package/src/authentication/authentication.ts +107 -0
- package/src/authentication/index.ts +1 -0
- package/src/chat/chat.ts +160 -0
- package/src/chat/index.ts +6 -0
- package/src/chat/interfaces/events.ts +269 -0
- package/src/chat/interfaces/index.ts +9 -0
- package/src/chat/parser.ts +265 -0
- package/src/eventsub/events/channel.ad_break.begin.ts +51 -0
- package/src/eventsub/events/channel.ban.ts +59 -0
- package/src/eventsub/events/channel.channel_points_custom_reward._.ts +29 -0
- package/src/eventsub/events/channel.channel_points_custom_reward.add.ts +75 -0
- package/src/eventsub/events/channel.channel_points_custom_reward.remove.ts +77 -0
- package/src/eventsub/events/channel.channel_points_custom_reward.update.ts +77 -0
- package/src/eventsub/events/channel.channel_points_custom_reward_redemption._.ts +10 -0
- package/src/eventsub/events/channel.channel_points_custom_reward_redemption.add.ts +59 -0
- package/src/eventsub/events/channel.channel_points_custom_reward_redemption.update.ts +59 -0
- package/src/eventsub/events/channel.charity_campaign.donate.ts +66 -0
- package/src/eventsub/events/channel.charity_campaign.progress.ts +67 -0
- package/src/eventsub/events/channel.charity_campaign.start.ts +69 -0
- package/src/eventsub/events/channel.charity_campaign.stop.ts +69 -0
- package/src/eventsub/events/channel.chat.clear.ts +42 -0
- package/src/eventsub/events/channel.chat.clear_user_messages.ts +48 -0
- package/src/eventsub/events/channel.chat.message_delete.ts +50 -0
- package/src/eventsub/events/channel.chat.notification.ts +50 -0
- package/src/eventsub/events/channel.cheer.ts +51 -0
- package/src/eventsub/events/channel.follow.ts +50 -0
- package/src/eventsub/events/channel.goal.begin.ts +51 -0
- package/src/eventsub/events/channel.goal.end.ts +55 -0
- package/src/eventsub/events/channel.goal.progress.ts +51 -0
- package/src/eventsub/events/channel.guest_star_guest.update.ts +66 -0
- package/src/eventsub/events/channel.guest_star_session.begin.ts +46 -0
- package/src/eventsub/events/channel.guest_star_session.end.ts +48 -0
- package/src/eventsub/events/channel.guest_star_settings.update.ts +50 -0
- package/src/eventsub/events/channel.hype_train._.ts +12 -0
- package/src/eventsub/events/channel.hype_train.begin.ts +57 -0
- package/src/eventsub/events/channel.hype_train.end.ts +55 -0
- package/src/eventsub/events/channel.hype_train.progress.ts +70 -0
- package/src/eventsub/events/channel.moderator.add.ts +45 -0
- package/src/eventsub/events/channel.moderator.remove.ts +45 -0
- package/src/eventsub/events/channel.poll._.ts +26 -0
- package/src/eventsub/events/channel.poll.begin.ts +55 -0
- package/src/eventsub/events/channel.poll.end.ts +57 -0
- package/src/eventsub/events/channel.poll.progress.ts +55 -0
- package/src/eventsub/events/channel.prediction._.ts +25 -0
- package/src/eventsub/events/channel.prediction.begin.ts +51 -0
- package/src/eventsub/events/channel.prediction.end.ts +55 -0
- package/src/eventsub/events/channel.prediction.lock.ts +51 -0
- package/src/eventsub/events/channel.prediction.progress.ts +51 -0
- package/src/eventsub/events/channel.raid.ts +49 -0
- package/src/eventsub/events/channel.shield_mode.begin.ts +50 -0
- package/src/eventsub/events/channel.shield_mode.end.ts +50 -0
- package/src/eventsub/events/channel.shoutout.create.ts +62 -0
- package/src/eventsub/events/channel.shoutout.receive.ts +52 -0
- package/src/eventsub/events/channel.subscribe.ts +49 -0
- package/src/eventsub/events/channel.subscription.end.ts +49 -0
- package/src/eventsub/events/channel.subscription.gift.ts +53 -0
- package/src/eventsub/events/channel.subscription.message.ts +67 -0
- package/src/eventsub/events/channel.unban.ts +51 -0
- package/src/eventsub/events/channel.update.ts +49 -0
- package/src/eventsub/events/index.ts +284 -0
- package/src/eventsub/events/stream.offline.ts +39 -0
- package/src/eventsub/events/stream.online.ts +45 -0
- package/src/eventsub/events/user.update.ts +45 -0
- package/src/eventsub/events-helpers.ts +29 -0
- package/src/eventsub/eventsub.ts +88 -0
- package/src/eventsub/index.ts +7 -0
- package/src/eventsub/messages.ts +34 -0
- package/src/helix/channel-points/custom-rewards.ts +63 -0
- package/src/helix/channel-points/index.ts +1 -0
- package/src/helix/helix.ts +89 -0
- package/src/helix/subscriptions/index.ts +1 -0
- package/src/helix/subscriptions/subscriptions.ts +122 -0
- package/src/helix/users/index.ts +1 -0
- package/src/helix/users/users.ts +42 -0
- package/src/helpers/alerts/alerts.ts +60 -0
- package/src/helpers/alerts/index.ts +2 -0
- package/src/helpers/charity/charity.ts +89 -0
- package/src/helpers/charity/index.ts +1 -0
- package/src/helpers/goal/goal.ts +38 -0
- package/src/helpers/goal/index.ts +1 -0
- package/src/helpers/hype-train/hype-train.ts +51 -0
- package/src/helpers/hype-train/index.ts +1 -0
- package/src/helpers/poll/index.ts +1 -0
- package/src/helpers/poll/poll.ts +63 -0
- package/src/helpers/prediction/index.ts +1 -0
- package/src/helpers/prediction/prediction.ts +66 -0
- package/src/helpers/redemption/index.ts +1 -0
- package/src/helpers/redemption/redemption.ts +42 -0
- package/src/helpers/status/index.ts +1 -0
- package/src/helpers/status/status.ts +61 -0
- package/src/setupTests.ts +0 -0
- package/src/ui/authentication.ts +230 -0
- package/src/ui/popup.ts +115 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import querystring from "@overlaysymphony/core/libs/querystring"
|
|
2
|
+
|
|
3
|
+
import { Authentication } from "../authentication/index.js"
|
|
4
|
+
|
|
5
|
+
export async function helix<
|
|
6
|
+
Data = never,
|
|
7
|
+
RawData = never,
|
|
8
|
+
Params = unknown,
|
|
9
|
+
Body = never,
|
|
10
|
+
>(
|
|
11
|
+
authentication: Authentication,
|
|
12
|
+
{
|
|
13
|
+
method,
|
|
14
|
+
path,
|
|
15
|
+
params,
|
|
16
|
+
body,
|
|
17
|
+
}: {
|
|
18
|
+
method: string
|
|
19
|
+
path: string
|
|
20
|
+
params?: Params
|
|
21
|
+
body?: Body
|
|
22
|
+
},
|
|
23
|
+
transform: (data: RawData) => Data = (data) => data as unknown as Data,
|
|
24
|
+
): Promise<Data[]> {
|
|
25
|
+
const queryString = params ? querystring.stringify(params) : ""
|
|
26
|
+
const { bodyString, contentType } = getBodyString(body)
|
|
27
|
+
|
|
28
|
+
const url = `https://api.twitch.tv/helix${path}?${queryString}`
|
|
29
|
+
const response = await fetch(url, {
|
|
30
|
+
method,
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: `Bearer ${authentication.accessToken}`,
|
|
33
|
+
"Client-Id": authentication.clientId,
|
|
34
|
+
"Content-Type": contentType,
|
|
35
|
+
},
|
|
36
|
+
body: bodyString,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
console.error({ method, path, params, body })
|
|
41
|
+
throw new Error(await response.text())
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const text = await response.text()
|
|
45
|
+
if (!text) {
|
|
46
|
+
return undefined as unknown as Data[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
data,
|
|
51
|
+
// pagination: { cursor },
|
|
52
|
+
} = JSON.parse(text) as {
|
|
53
|
+
total: number
|
|
54
|
+
data: RawData[]
|
|
55
|
+
pagination: {
|
|
56
|
+
cursor: string
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// if (cursor) {
|
|
61
|
+
// return data.concat(await helix<Data>(method, path, params, cursor))
|
|
62
|
+
// }
|
|
63
|
+
|
|
64
|
+
return data.map((data) => transform(data))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getBodyString<Body>(body: Body): {
|
|
68
|
+
bodyString: string
|
|
69
|
+
contentType: string
|
|
70
|
+
} {
|
|
71
|
+
if (typeof body === "string") {
|
|
72
|
+
return {
|
|
73
|
+
bodyString: body,
|
|
74
|
+
contentType: "application/x-www-form-urlencoded",
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (typeof body === "object") {
|
|
79
|
+
return {
|
|
80
|
+
bodyString: JSON.stringify(body),
|
|
81
|
+
contentType: "application/json",
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
bodyString: body as string,
|
|
87
|
+
contentType: undefined as unknown as string,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./subscriptions.js"
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Authentication } from "../../authentication/index.js"
|
|
2
|
+
import {
|
|
3
|
+
TwitchSubscription,
|
|
4
|
+
TwitchSubscriptionType,
|
|
5
|
+
buildSubscription,
|
|
6
|
+
} from "../../eventsub/events/index.js"
|
|
7
|
+
import { BaseSubscription } from "../../eventsub/events-helpers.js"
|
|
8
|
+
import { helix } from "../helix.js"
|
|
9
|
+
|
|
10
|
+
interface SubscriptionWebhookTransport {
|
|
11
|
+
method: "webhook"
|
|
12
|
+
callback: string
|
|
13
|
+
secret: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface SubscriptionWebsocketTransport {
|
|
17
|
+
method: "websocket"
|
|
18
|
+
session_id: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type SubscriptionTransport =
|
|
22
|
+
| SubscriptionWebhookTransport
|
|
23
|
+
| SubscriptionWebsocketTransport
|
|
24
|
+
|
|
25
|
+
export type SubscriptionRequest<Subscription extends BaseSubscription> = {
|
|
26
|
+
type: Subscription["type"]
|
|
27
|
+
version: Subscription["version"]
|
|
28
|
+
condition: Subscription["condition"]
|
|
29
|
+
transport: SubscriptionTransport
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ActiveSubscription<Subscription extends BaseSubscription> =
|
|
33
|
+
Subscription & {
|
|
34
|
+
id: string
|
|
35
|
+
status:
|
|
36
|
+
| "enabled"
|
|
37
|
+
| "webhook_callback_verification_pending"
|
|
38
|
+
| "webhook_callback_verification_failed"
|
|
39
|
+
| "notification_failures_exceeded"
|
|
40
|
+
| "authorization_revoked"
|
|
41
|
+
| "moderator_removed"
|
|
42
|
+
| "user_removed"
|
|
43
|
+
| "version_removed"
|
|
44
|
+
| "websocket_disconnected"
|
|
45
|
+
| "websocket_failed_ping_pong"
|
|
46
|
+
| "websocket_received_inbound_traffic"
|
|
47
|
+
| "websocket_connection_unused"
|
|
48
|
+
| "websocket_internal_error"
|
|
49
|
+
| "websocket_network_timeout"
|
|
50
|
+
| "websocket_network_error"
|
|
51
|
+
cost: number
|
|
52
|
+
created_at: Date
|
|
53
|
+
transport: SubscriptionTransport
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function createSubscription<
|
|
57
|
+
Type extends TwitchSubscriptionType,
|
|
58
|
+
Subscription extends TwitchSubscription<Type>,
|
|
59
|
+
>(
|
|
60
|
+
sessionId: string,
|
|
61
|
+
authentication: Authentication,
|
|
62
|
+
type: Type,
|
|
63
|
+
): Promise<ActiveSubscription<Subscription>> {
|
|
64
|
+
const subscription = buildSubscription(
|
|
65
|
+
type,
|
|
66
|
+
authentication.user.id,
|
|
67
|
+
) as Subscription
|
|
68
|
+
|
|
69
|
+
const [activeSubscription] = await helix<
|
|
70
|
+
ActiveSubscription<Subscription>,
|
|
71
|
+
never,
|
|
72
|
+
never,
|
|
73
|
+
SubscriptionRequest<Subscription>
|
|
74
|
+
>(authentication, {
|
|
75
|
+
method: "post",
|
|
76
|
+
path: "/eventsub/subscriptions",
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
body: {
|
|
79
|
+
...subscription,
|
|
80
|
+
transport: {
|
|
81
|
+
method: "websocket" as const,
|
|
82
|
+
session_id: sessionId,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
return activeSubscription
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function deleteSubscription(
|
|
91
|
+
authentication: Authentication,
|
|
92
|
+
id: string,
|
|
93
|
+
): Promise<void> {
|
|
94
|
+
await helix<never, never, { id: string }, never>(authentication, {
|
|
95
|
+
method: "delete",
|
|
96
|
+
path: "/eventsub/subscriptions",
|
|
97
|
+
params: {
|
|
98
|
+
id,
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function getSubscriptions(
|
|
104
|
+
authentication: Authentication,
|
|
105
|
+
): Promise<Array<ActiveSubscription<TwitchSubscription>>> {
|
|
106
|
+
const subscriptions = await helix<
|
|
107
|
+
ActiveSubscription<TwitchSubscription>,
|
|
108
|
+
never,
|
|
109
|
+
{
|
|
110
|
+
status?: ActiveSubscription<TwitchSubscription>["status"]
|
|
111
|
+
type?: TwitchSubscriptionType
|
|
112
|
+
user_id?: string
|
|
113
|
+
after?: string
|
|
114
|
+
},
|
|
115
|
+
never
|
|
116
|
+
>(authentication, {
|
|
117
|
+
method: "get",
|
|
118
|
+
path: "/eventsub/subscriptions",
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
return subscriptions
|
|
122
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./users.js"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Authentication } from "../../authentication/index.js"
|
|
2
|
+
import { helix } from "../helix.js"
|
|
3
|
+
|
|
4
|
+
export interface TwitchUser {
|
|
5
|
+
id: string
|
|
6
|
+
login: string
|
|
7
|
+
display_name: string
|
|
8
|
+
type: "admin" | "global_mod" | "staff" | ""
|
|
9
|
+
broadcaster_type: "partner" | "affiliate" | ""
|
|
10
|
+
description: string
|
|
11
|
+
profile_image_url: string
|
|
12
|
+
offline_image_url: string
|
|
13
|
+
created_at: Date
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type RawUser = Omit<TwitchUser, "created_at"> & {
|
|
17
|
+
created_at: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function getUsers(
|
|
21
|
+
authentication: Authentication,
|
|
22
|
+
id?: string | string[],
|
|
23
|
+
login?: string | string[],
|
|
24
|
+
): Promise<TwitchUser[]> {
|
|
25
|
+
const data = await helix<TwitchUser, RawUser>(
|
|
26
|
+
authentication,
|
|
27
|
+
{
|
|
28
|
+
method: "get",
|
|
29
|
+
path: "/users",
|
|
30
|
+
params: {
|
|
31
|
+
id,
|
|
32
|
+
login,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
({ created_at, ...data }) => ({
|
|
36
|
+
...data,
|
|
37
|
+
created_at: new Date(created_at),
|
|
38
|
+
}),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return data
|
|
42
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import createQueue, { Queue } from "@overlaysymphony/core/libs/queue"
|
|
2
|
+
|
|
3
|
+
import { TwitchNotificationMessage } from "../../eventsub/events/index.js"
|
|
4
|
+
import { TwitchEventSub } from "../../eventsub/index.js"
|
|
5
|
+
|
|
6
|
+
export type Alert = TwitchNotificationMessage<
|
|
7
|
+
| "channel.cheer"
|
|
8
|
+
| "channel.follow"
|
|
9
|
+
| "channel.raid"
|
|
10
|
+
| "channel.subscribe"
|
|
11
|
+
| "channel.subscription.gift"
|
|
12
|
+
| "channel.subscription.message"
|
|
13
|
+
>["payload"]
|
|
14
|
+
|
|
15
|
+
export const mapTypeToPriority = {
|
|
16
|
+
"channel.follow": 0,
|
|
17
|
+
"channel.cheer": 1,
|
|
18
|
+
"channel.subscribe": 2,
|
|
19
|
+
"channel.subscription.message": 3,
|
|
20
|
+
"channel.subscription.gift": 4,
|
|
21
|
+
"channel.raid": 5,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function onAlert(
|
|
25
|
+
eventsub: TwitchEventSub,
|
|
26
|
+
handleAlert: (alert: Alert) => void,
|
|
27
|
+
): void {
|
|
28
|
+
eventsub.subscribe(
|
|
29
|
+
[
|
|
30
|
+
"channel.cheer",
|
|
31
|
+
"channel.follow",
|
|
32
|
+
"channel.raid",
|
|
33
|
+
"channel.subscribe",
|
|
34
|
+
"channel.subscription.gift",
|
|
35
|
+
"channel.subscription.message",
|
|
36
|
+
],
|
|
37
|
+
(payload) => {
|
|
38
|
+
// Don't spam alerts when gifted many subs
|
|
39
|
+
if (payload.type === "channel.subscribe" && payload.event.is_gift) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
handleAlert(payload)
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default function createAlertQueue(
|
|
49
|
+
eventsub: TwitchEventSub,
|
|
50
|
+
handleAlert: (alert: Alert) => void,
|
|
51
|
+
): Queue<Alert>["dismiss"] {
|
|
52
|
+
const queue = createQueue<Alert>()
|
|
53
|
+
queue.listen(handleAlert)
|
|
54
|
+
|
|
55
|
+
onAlert(eventsub, (payload) => {
|
|
56
|
+
queue.enqueue(mapTypeToPriority[payload.type], payload)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return queue.dismiss
|
|
60
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { TwitchEventSub } from "../../eventsub/index.js"
|
|
2
|
+
|
|
3
|
+
export interface Charity {
|
|
4
|
+
name: string
|
|
5
|
+
description: string
|
|
6
|
+
logo: string
|
|
7
|
+
website: string
|
|
8
|
+
currency: string
|
|
9
|
+
currentAmount: number
|
|
10
|
+
targetAmount: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CharityDonation {
|
|
14
|
+
userId: string
|
|
15
|
+
userLogin: string
|
|
16
|
+
userName: string
|
|
17
|
+
amount: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const mapTypeToTrigger = {
|
|
21
|
+
"channel.charity_campaign.donate": "donate",
|
|
22
|
+
"channel.charity_campaign.progress": "progress",
|
|
23
|
+
"channel.charity_campaign.start": "start",
|
|
24
|
+
"channel.charity_campaign.stop": "stop",
|
|
25
|
+
} as const
|
|
26
|
+
|
|
27
|
+
export function onCharity(
|
|
28
|
+
eventsub: TwitchEventSub,
|
|
29
|
+
handleCharity: (
|
|
30
|
+
charity: Charity,
|
|
31
|
+
trigger: "progress" | "start" | "stop",
|
|
32
|
+
) => void,
|
|
33
|
+
handleDonation: (donation: CharityDonation, charity: Charity) => void,
|
|
34
|
+
): void {
|
|
35
|
+
const charity: Charity = {
|
|
36
|
+
name: "",
|
|
37
|
+
description: "",
|
|
38
|
+
logo: "",
|
|
39
|
+
website: "",
|
|
40
|
+
currency: "",
|
|
41
|
+
currentAmount: 0,
|
|
42
|
+
targetAmount: 0,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
eventsub.subscribe(
|
|
46
|
+
[
|
|
47
|
+
"channel.charity_campaign.start",
|
|
48
|
+
"channel.charity_campaign.progress",
|
|
49
|
+
"channel.charity_campaign.stop",
|
|
50
|
+
"channel.charity_campaign.donate",
|
|
51
|
+
],
|
|
52
|
+
(payload) => {
|
|
53
|
+
charity.name = payload.event.charity_name
|
|
54
|
+
charity.description = payload.event.charity_description
|
|
55
|
+
charity.logo = payload.event.charity_logo
|
|
56
|
+
charity.website = payload.event.charity_website
|
|
57
|
+
|
|
58
|
+
if (
|
|
59
|
+
payload.type === "channel.charity_campaign.start" ||
|
|
60
|
+
payload.type === "channel.charity_campaign.progress" ||
|
|
61
|
+
payload.type === "channel.charity_campaign.stop"
|
|
62
|
+
) {
|
|
63
|
+
charity.currency = payload.event.target_amount.currency
|
|
64
|
+
charity.currentAmount =
|
|
65
|
+
payload.event.current_amount.value /
|
|
66
|
+
10 ** payload.event.current_amount.decimal_places
|
|
67
|
+
charity.targetAmount =
|
|
68
|
+
payload.event.target_amount.value /
|
|
69
|
+
10 ** payload.event.target_amount.decimal_places
|
|
70
|
+
|
|
71
|
+
handleCharity(charity, mapTypeToTrigger[payload.type])
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (payload.type === "channel.charity_campaign.donate") {
|
|
75
|
+
handleDonation(
|
|
76
|
+
{
|
|
77
|
+
userId: payload.event.user_id,
|
|
78
|
+
userLogin: payload.event.user_login,
|
|
79
|
+
userName: payload.event.user_name,
|
|
80
|
+
amount:
|
|
81
|
+
payload.event.amount.value /
|
|
82
|
+
10 ** payload.event.amount.decimal_places,
|
|
83
|
+
},
|
|
84
|
+
charity,
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./charity.js"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { TwitchEventSub } from "../../eventsub/index.js"
|
|
2
|
+
|
|
3
|
+
export interface Goal {
|
|
4
|
+
type: string
|
|
5
|
+
description: string
|
|
6
|
+
currentAmount: number
|
|
7
|
+
targetAmount: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const mapTypeToTrigger = {
|
|
11
|
+
"channel.goal.begin": "begin",
|
|
12
|
+
"channel.goal.progress": "progress",
|
|
13
|
+
"channel.goal.end": "end",
|
|
14
|
+
} as const
|
|
15
|
+
|
|
16
|
+
export function onGoal(
|
|
17
|
+
eventsub: TwitchEventSub,
|
|
18
|
+
handleGoal: (goal: Goal, trigger: "begin" | "progress" | "end") => void,
|
|
19
|
+
): void {
|
|
20
|
+
const goal: Goal = {
|
|
21
|
+
type: "",
|
|
22
|
+
description: "",
|
|
23
|
+
currentAmount: 0,
|
|
24
|
+
targetAmount: 0,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
eventsub.subscribe(
|
|
28
|
+
["channel.goal.begin", "channel.goal.progress", "channel.goal.end"],
|
|
29
|
+
(payload) => {
|
|
30
|
+
goal.type = payload.event.type
|
|
31
|
+
goal.description = payload.event.description
|
|
32
|
+
goal.currentAmount = payload.event.current_amount
|
|
33
|
+
goal.targetAmount = payload.event.target_amount
|
|
34
|
+
|
|
35
|
+
handleGoal(goal, mapTypeToTrigger[payload.type])
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./goal.js"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { TwitchEventSub } from "../../eventsub/index.js"
|
|
2
|
+
|
|
3
|
+
export interface HypeTrain {
|
|
4
|
+
level: number
|
|
5
|
+
total: number
|
|
6
|
+
progress: number
|
|
7
|
+
goal: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const mapTypeToTrigger = {
|
|
11
|
+
"channel.hype_train.begin": "begin",
|
|
12
|
+
"channel.hype_train.progress": "progress",
|
|
13
|
+
"channel.hype_train.end": "end",
|
|
14
|
+
} as const
|
|
15
|
+
|
|
16
|
+
export function onHypeTrain(
|
|
17
|
+
eventsub: TwitchEventSub,
|
|
18
|
+
handleHypeTrain: (
|
|
19
|
+
hypeTrain: HypeTrain,
|
|
20
|
+
trigger: "begin" | "progress" | "end",
|
|
21
|
+
) => void,
|
|
22
|
+
): void {
|
|
23
|
+
const hypeTrain: HypeTrain = {
|
|
24
|
+
level: 0,
|
|
25
|
+
total: 0,
|
|
26
|
+
progress: 0,
|
|
27
|
+
goal: 0,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
eventsub.subscribe(
|
|
31
|
+
[
|
|
32
|
+
"channel.hype_train.begin",
|
|
33
|
+
"channel.hype_train.progress",
|
|
34
|
+
"channel.hype_train.end",
|
|
35
|
+
],
|
|
36
|
+
(payload) => {
|
|
37
|
+
hypeTrain.level = payload.event.level
|
|
38
|
+
hypeTrain.total = payload.event.total
|
|
39
|
+
|
|
40
|
+
if (
|
|
41
|
+
payload.type === "channel.hype_train.begin" ||
|
|
42
|
+
payload.type === "channel.hype_train.progress"
|
|
43
|
+
) {
|
|
44
|
+
hypeTrain.progress = payload.event.progress
|
|
45
|
+
hypeTrain.goal = payload.event.goal
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
handleHypeTrain(hypeTrain, mapTypeToTrigger[payload.type])
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./hype-train.js"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./poll.js"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { TwitchEventSub } from "../../eventsub/index.js"
|
|
2
|
+
|
|
3
|
+
export interface Poll {
|
|
4
|
+
title: string
|
|
5
|
+
choices: Array<{
|
|
6
|
+
id: string
|
|
7
|
+
title: string
|
|
8
|
+
votes: number
|
|
9
|
+
votesBits: number
|
|
10
|
+
votesChannelPoints: number
|
|
11
|
+
votesNormal: number
|
|
12
|
+
}>
|
|
13
|
+
endsAt: Date
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const mapTypeToTrigger = {
|
|
17
|
+
"channel.poll.begin": "begin",
|
|
18
|
+
"channel.poll.progress": "progress",
|
|
19
|
+
"channel.poll.end": "end",
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
export function onPoll(
|
|
23
|
+
eventsub: TwitchEventSub,
|
|
24
|
+
handlePoll: (poll: Poll, trigger: "begin" | "progress" | "end") => void,
|
|
25
|
+
): void {
|
|
26
|
+
const poll: Poll = {
|
|
27
|
+
title: "",
|
|
28
|
+
choices: [],
|
|
29
|
+
endsAt: new Date(""),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
eventsub.subscribe(
|
|
33
|
+
["channel.poll.begin", "channel.poll.progress", "channel.poll.end"],
|
|
34
|
+
(payload) => {
|
|
35
|
+
poll.title = payload.event.title
|
|
36
|
+
poll.choices = payload.event.choices.map(
|
|
37
|
+
({
|
|
38
|
+
id,
|
|
39
|
+
title,
|
|
40
|
+
votes = 0,
|
|
41
|
+
channel_points_votes = 0,
|
|
42
|
+
bits_votes = 0,
|
|
43
|
+
}) => ({
|
|
44
|
+
id,
|
|
45
|
+
title,
|
|
46
|
+
votes: votes,
|
|
47
|
+
votesBits: bits_votes,
|
|
48
|
+
votesChannelPoints: channel_points_votes,
|
|
49
|
+
votesNormal: votes - channel_points_votes - bits_votes,
|
|
50
|
+
}),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if (
|
|
54
|
+
payload.type === "channel.poll.begin" ||
|
|
55
|
+
payload.type === "channel.poll.progress"
|
|
56
|
+
) {
|
|
57
|
+
poll.endsAt = payload.event.ends_at
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
handlePoll(poll, mapTypeToTrigger[payload.type])
|
|
61
|
+
},
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./prediction.js"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { TwitchEventSub } from "../../eventsub/index.js"
|
|
2
|
+
|
|
3
|
+
export interface Prediction {
|
|
4
|
+
title: string
|
|
5
|
+
outcomes: Array<{
|
|
6
|
+
id: string
|
|
7
|
+
title: string
|
|
8
|
+
color: string
|
|
9
|
+
users: number
|
|
10
|
+
points: number
|
|
11
|
+
}>
|
|
12
|
+
locksAt?: Date
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const mapTypeToTrigger = {
|
|
16
|
+
"channel.prediction.begin": "begin",
|
|
17
|
+
"channel.prediction.progress": "progress",
|
|
18
|
+
"channel.prediction.lock": "lock",
|
|
19
|
+
"channel.prediction.end": "end",
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
export function onPrediction(
|
|
23
|
+
eventsub: TwitchEventSub,
|
|
24
|
+
handlePrediction: (
|
|
25
|
+
prediction: Prediction,
|
|
26
|
+
trigger: "begin" | "progress" | "lock" | "end",
|
|
27
|
+
) => void,
|
|
28
|
+
): void {
|
|
29
|
+
const prediction: Prediction = {
|
|
30
|
+
title: "",
|
|
31
|
+
outcomes: [],
|
|
32
|
+
locksAt: new Date(""),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
eventsub.subscribe(
|
|
36
|
+
[
|
|
37
|
+
"channel.prediction.begin",
|
|
38
|
+
"channel.prediction.progress",
|
|
39
|
+
"channel.prediction.lock",
|
|
40
|
+
"channel.prediction.end",
|
|
41
|
+
],
|
|
42
|
+
(payload) => {
|
|
43
|
+
prediction.title = payload.event.title
|
|
44
|
+
prediction.outcomes = payload.event.outcomes.map(
|
|
45
|
+
({ id, title, color, users, channel_points }) => ({
|
|
46
|
+
id,
|
|
47
|
+
title,
|
|
48
|
+
color,
|
|
49
|
+
users,
|
|
50
|
+
points: channel_points,
|
|
51
|
+
}),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
payload.type === "channel.prediction.begin" ||
|
|
56
|
+
payload.type === "channel.prediction.progress"
|
|
57
|
+
) {
|
|
58
|
+
prediction.locksAt = payload.event.locks_at
|
|
59
|
+
} else {
|
|
60
|
+
prediction.locksAt = undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
handlePrediction(prediction, mapTypeToTrigger[payload.type])
|
|
64
|
+
},
|
|
65
|
+
)
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./redemption.js"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { TwitchEventSub } from "../../eventsub/index.js"
|
|
2
|
+
|
|
3
|
+
export interface Redemption {
|
|
4
|
+
id: string
|
|
5
|
+
userId: string
|
|
6
|
+
userLogin: string
|
|
7
|
+
userName: string
|
|
8
|
+
userInput: string
|
|
9
|
+
reward: {
|
|
10
|
+
id: string
|
|
11
|
+
title: string
|
|
12
|
+
cost: number
|
|
13
|
+
prompt: string
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function onRedemption(
|
|
18
|
+
eventsub: TwitchEventSub,
|
|
19
|
+
id: string,
|
|
20
|
+
handleRedemption: (redemption: Redemption) => void,
|
|
21
|
+
): void {
|
|
22
|
+
eventsub.subscribe(
|
|
23
|
+
["channel.channel_points_custom_reward_redemption.add"],
|
|
24
|
+
(payload) => {
|
|
25
|
+
if (payload.event.reward.id === id) {
|
|
26
|
+
handleRedemption({
|
|
27
|
+
id: payload.event.id,
|
|
28
|
+
userId: payload.event.user_id,
|
|
29
|
+
userLogin: payload.event.user_login,
|
|
30
|
+
userName: payload.event.user_name,
|
|
31
|
+
userInput: payload.event.user_input,
|
|
32
|
+
reward: {
|
|
33
|
+
id: payload.event.reward.id,
|
|
34
|
+
title: payload.event.reward.title,
|
|
35
|
+
cost: payload.event.reward.cost,
|
|
36
|
+
prompt: payload.event.reward.prompt,
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./status.js"
|