@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
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# `@overlaysymphony/obs`
|
|
2
|
+
|
|
3
|
+
This module enables [Twitch](https://dev.twitch.tv/docs/) integrations for the [OverlaySymphony interactive streaming framework](https://github.com/OverlaySymphony/overlaysymphony), including authentication, chat, eventsub, helix, and several helpers.
|
|
4
|
+
|
|
5
|
+
# OverlaySymphony
|
|
6
|
+
|
|
7
|
+
An interactive streaming framework for orchestrating overlays, bots, and other services.
|
|
8
|
+
|
|
9
|
+
> Note: This project is still very much under development. I use it actively on my own stream, so all releases should be fully functional. I will do my best to follow semver, though I cannot make any guarantees about release timelines or stability at this time.
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@overlaysymphony/twitch",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Twitch module for the OverlaySymphony interactive streaming framework.",
|
|
5
|
+
"homepage": "https://github.com/OverlaySymphony/overlaysymphony",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
"./authentication": "./src/authentication/index.js",
|
|
9
|
+
"./chat": "./src/chat/index.js",
|
|
10
|
+
"./eventsub": "./src/eventsub/index.js",
|
|
11
|
+
"./helix/*": "./src/helix/*/index.js",
|
|
12
|
+
"./helpers/*": "./src/helpers/*/index.js",
|
|
13
|
+
"./ui/*": "./src/ui/*.js",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"lint": "pnpm run \"/^lint-.*/\"",
|
|
18
|
+
"lint-typecheck": "tsc --noEmit",
|
|
19
|
+
"lint-eslint": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx .",
|
|
20
|
+
"lint-prettier": "prettier --check .",
|
|
21
|
+
"lint-depcheck": "depcheck .",
|
|
22
|
+
"test": "vitest",
|
|
23
|
+
"dev": "echo TODO",
|
|
24
|
+
"clean": "rm -rf node_modules/.cache tsconfig.tsbuildinfo dist",
|
|
25
|
+
"build": "echo TODO"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@overlaysymphony/core": "workspace:*"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@overlaysymphony/eslint-config": "workspace:*",
|
|
32
|
+
"@overlaysymphony/tooling": "workspace:*",
|
|
33
|
+
"@vitest/coverage-v8": "^2.1.2",
|
|
34
|
+
"depcheck": "catalog:",
|
|
35
|
+
"eslint": "catalog:",
|
|
36
|
+
"prettier": "catalog:",
|
|
37
|
+
"typescript": "catalog:",
|
|
38
|
+
"vitest": "^2.1.2"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import querystring from "@overlaysymphony/core/libs/querystring"
|
|
2
|
+
|
|
3
|
+
import { TwitchUser, getUsers } from "../helix/users/index.js"
|
|
4
|
+
|
|
5
|
+
export interface Authentication {
|
|
6
|
+
clientId: string
|
|
7
|
+
tokenType: "bearer"
|
|
8
|
+
accessToken: string
|
|
9
|
+
scope: string[]
|
|
10
|
+
expires: Date
|
|
11
|
+
user: TwitchUser
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type BareAuthentication = Omit<Authentication, "user">
|
|
15
|
+
|
|
16
|
+
interface TAValidation {
|
|
17
|
+
client_id: string
|
|
18
|
+
login: string
|
|
19
|
+
scope: string[]
|
|
20
|
+
user_id: string
|
|
21
|
+
expires_in: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface TAResult {
|
|
25
|
+
token_type: "bearer"
|
|
26
|
+
access_token: string
|
|
27
|
+
scope: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const localStorageKey = "overlaysymphony:service:twitch"
|
|
31
|
+
|
|
32
|
+
export async function getAuthentication(): Promise<Authentication> {
|
|
33
|
+
const raw = localStorage.getItem(localStorageKey)
|
|
34
|
+
if (!raw) {
|
|
35
|
+
throw new Error("Twitch authentication missing!")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const cached = JSON.parse(raw) as BareAuthentication
|
|
39
|
+
cached.expires = new Date(cached.expires)
|
|
40
|
+
|
|
41
|
+
const [user] = await getUsers(cached as Authentication)
|
|
42
|
+
|
|
43
|
+
const authentication: Authentication = {
|
|
44
|
+
...cached,
|
|
45
|
+
user,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return authentication
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function initiateAuthentication(
|
|
52
|
+
clientId: string,
|
|
53
|
+
redirect: string,
|
|
54
|
+
scope: string[],
|
|
55
|
+
): string {
|
|
56
|
+
return `https://id.twitch.tv/oauth2/authorize?${querystring.stringify({
|
|
57
|
+
response_type: "token",
|
|
58
|
+
client_id: clientId,
|
|
59
|
+
redirect_uri: redirect,
|
|
60
|
+
scope: scope.join("+"),
|
|
61
|
+
state: clientId,
|
|
62
|
+
})}`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function validateAuthentication(
|
|
66
|
+
authentication: Omit<BareAuthentication, "expires">,
|
|
67
|
+
): Promise<BareAuthentication> {
|
|
68
|
+
const response = await fetch("https://id.twitch.tv/oauth2/validate", {
|
|
69
|
+
method: "GET",
|
|
70
|
+
headers: {
|
|
71
|
+
Authorization: `Bearer ${authentication.accessToken}`,
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error(await response.text())
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const validated = (await response.json()) as TAValidation
|
|
80
|
+
|
|
81
|
+
if (validated.client_id !== authentication.clientId) {
|
|
82
|
+
throw new Error("ClientId mismatch.")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
...authentication,
|
|
87
|
+
expires: new Date(Date.now() + validated.expires_in * 1000),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function authenticateResult(
|
|
92
|
+
clientId: string,
|
|
93
|
+
result: TAResult,
|
|
94
|
+
): Promise<BareAuthentication> {
|
|
95
|
+
if (!result.token_type || !result.access_token || !result.scope) {
|
|
96
|
+
throw new Error("Invalid result.")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const authentication = await validateAuthentication({
|
|
100
|
+
clientId,
|
|
101
|
+
tokenType: result.token_type,
|
|
102
|
+
accessToken: result.access_token,
|
|
103
|
+
scope: result.scope.split("+"),
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return authentication
|
|
107
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./authentication.js"
|
package/src/chat/chat.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import createDefer from "@overlaysymphony/core/libs/defer"
|
|
2
|
+
import createPubSub from "@overlaysymphony/core/libs/pubsub"
|
|
3
|
+
|
|
4
|
+
import { Authentication } from "../authentication/index.js"
|
|
5
|
+
|
|
6
|
+
import { TwitchChatEvent, TwitchChatEventType } from "./interfaces/index.js"
|
|
7
|
+
import parseCommand from "./parser.js"
|
|
8
|
+
|
|
9
|
+
type ChatListener = (callback: (event: TwitchChatEvent) => void) => () => void
|
|
10
|
+
|
|
11
|
+
type ChatSubscriber = <
|
|
12
|
+
EventType extends TwitchChatEventType,
|
|
13
|
+
Event extends TwitchChatEvent<EventType>,
|
|
14
|
+
>(
|
|
15
|
+
types: EventType[],
|
|
16
|
+
callback: (event: Event) => void,
|
|
17
|
+
) => () => void
|
|
18
|
+
|
|
19
|
+
type ChatSender = (message: string) => void
|
|
20
|
+
|
|
21
|
+
type ChatMessageSubscriber = (
|
|
22
|
+
callback: (event: TwitchChatEvent<"PRIVMSG">) => void,
|
|
23
|
+
) => () => void
|
|
24
|
+
|
|
25
|
+
type ChatCommandSubscriber = {
|
|
26
|
+
(
|
|
27
|
+
callback: (event: TwitchChatEvent<"PRIVMSG-COMMAND">) => void,
|
|
28
|
+
name?: never,
|
|
29
|
+
): () => void
|
|
30
|
+
(
|
|
31
|
+
name: string,
|
|
32
|
+
callback: (event: TwitchChatEvent<"PRIVMSG-COMMAND">) => void,
|
|
33
|
+
): () => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TwitchChat {
|
|
37
|
+
listen: ChatListener
|
|
38
|
+
subscribe: ChatSubscriber
|
|
39
|
+
send: ChatSender
|
|
40
|
+
onMessage: ChatMessageSubscriber
|
|
41
|
+
onCommand: ChatCommandSubscriber
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function createChat(
|
|
45
|
+
authentication: Authentication,
|
|
46
|
+
channel: string = authentication.user.login,
|
|
47
|
+
): Promise<TwitchChat> {
|
|
48
|
+
const { promise, resolve } = createDefer()
|
|
49
|
+
|
|
50
|
+
const pubsub = createPubSub<TwitchChatEvent>()
|
|
51
|
+
const socket = new WebSocket("wss://irc-ws.chat.twitch.tv:443")
|
|
52
|
+
|
|
53
|
+
socket.addEventListener("open", (connection) => {
|
|
54
|
+
socket.send("CAP REQ :twitch.tv/tags twitch.tv/commands")
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
socket.addEventListener("message", ({ data }: { data: string }) => {
|
|
58
|
+
const messages = data.split("\r\n").filter(Boolean)
|
|
59
|
+
for (const input of messages) {
|
|
60
|
+
const command = parseCommand(input)
|
|
61
|
+
if (!command) {
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
command.type === "001" ||
|
|
67
|
+
command.type === "JOIN" ||
|
|
68
|
+
command.type === "USERSTATE" ||
|
|
69
|
+
command.type === "HOSTTARGET" ||
|
|
70
|
+
command.type === "NOTICE" ||
|
|
71
|
+
false
|
|
72
|
+
) {
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (command.type === "PING") {
|
|
77
|
+
socket.send(`PONG :${command.message}`)
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (command.type === "CAP") {
|
|
82
|
+
socket.send(`PASS oauth:${authentication.accessToken}`)
|
|
83
|
+
socket.send(`NICK ${authentication.user.login}`)
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (command.type === "RECONNECT") {
|
|
88
|
+
console.warn("The server is about to terminate for maintenance.")
|
|
89
|
+
continue
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (command.type === "GLOBALUSERSTATE") {
|
|
93
|
+
socket.send(`JOIN #${channel}`)
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (command.type === "ROOMSTATE") {
|
|
98
|
+
resolve()
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pubsub.dispatch(command)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return promise.then(() => {
|
|
107
|
+
const listen: ChatListener = (callback) => {
|
|
108
|
+
return pubsub.subscribe((event) => {
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
callback(event)
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const subscribe: ChatSubscriber = (types, callback) => {
|
|
115
|
+
return pubsub.subscribe((event) => {
|
|
116
|
+
// @ts-ignore
|
|
117
|
+
if (types.includes(event.type)) {
|
|
118
|
+
// @ts-ignore
|
|
119
|
+
callback(event)
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const send: ChatSender = (message) => {
|
|
125
|
+
socket.send(`PRIVMSG #${authentication.user.login} :${message}`)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const onMessage: ChatMessageSubscriber = (callback) => {
|
|
129
|
+
return pubsub.subscribe((event) => {
|
|
130
|
+
if (event.type === "PRIVMSG") {
|
|
131
|
+
callback(event)
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const onCommand: ChatCommandSubscriber = (...args) => {
|
|
137
|
+
const name = typeof args[0] === "string" ? args[0] : undefined
|
|
138
|
+
const callback = typeof args[0] === "function" ? args[0] : args[1]
|
|
139
|
+
if (!callback) {
|
|
140
|
+
throw new Error("onCommand: Missing callback.")
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return pubsub.subscribe((event) => {
|
|
144
|
+
if (event.type === "PRIVMSG-COMMAND") {
|
|
145
|
+
if (typeof name === "undefined" || event.command === name) {
|
|
146
|
+
callback(event)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
listen,
|
|
154
|
+
subscribe,
|
|
155
|
+
send,
|
|
156
|
+
onMessage,
|
|
157
|
+
onCommand,
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
export interface ChatEventSource {
|
|
2
|
+
host: string
|
|
3
|
+
user?: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
type AllOrNothing<T> = T | Partial<Record<keyof T, undefined>>
|
|
7
|
+
|
|
8
|
+
type TagUserType = "staff" | "global_mod" | "admin" | undefined
|
|
9
|
+
type TagBadges = Record<string, number>
|
|
10
|
+
type TagBadgeInfo = Record<string, number>
|
|
11
|
+
type TagEmotes = Record<
|
|
12
|
+
string,
|
|
13
|
+
Array<[startPosition: string, endPosition: string]>
|
|
14
|
+
>
|
|
15
|
+
|
|
16
|
+
export type PingEvent = {
|
|
17
|
+
type: "PING"
|
|
18
|
+
message: string
|
|
19
|
+
source?: ChatEventSource
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type WelcomeEvent = {
|
|
23
|
+
type: "001" // Logged in (successfully authenticated)
|
|
24
|
+
channel: string
|
|
25
|
+
message: string
|
|
26
|
+
source?: ChatEventSource
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type CapabilitiesEvent = {
|
|
30
|
+
type: "CAP"
|
|
31
|
+
enabled: boolean
|
|
32
|
+
nickname: string
|
|
33
|
+
capabilities: string[]
|
|
34
|
+
source?: ChatEventSource
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type JoinEvent = {
|
|
38
|
+
type: "JOIN"
|
|
39
|
+
channel: string
|
|
40
|
+
source?: ChatEventSource
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type PartEvent = {
|
|
44
|
+
type: "PART"
|
|
45
|
+
channel: string
|
|
46
|
+
source?: ChatEventSource
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type GlobalUserStateEvent = {
|
|
50
|
+
type: "GLOBALUSERSTATE"
|
|
51
|
+
tags: {
|
|
52
|
+
userId: string
|
|
53
|
+
userType: TagUserType
|
|
54
|
+
displayName?: string
|
|
55
|
+
color?: string
|
|
56
|
+
badges?: TagBadges
|
|
57
|
+
badgeInfo?: TagBadgeInfo
|
|
58
|
+
emoteSets?: string[]
|
|
59
|
+
}
|
|
60
|
+
source?: ChatEventSource
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type ReconnectEvent = {
|
|
64
|
+
type: "RECONNECT"
|
|
65
|
+
source?: ChatEventSource
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type ClearChatEvent = {
|
|
69
|
+
type: "CLEARCHAT"
|
|
70
|
+
channel: string
|
|
71
|
+
tags: {
|
|
72
|
+
roomId: string
|
|
73
|
+
targetUserId: string
|
|
74
|
+
banDuration: number
|
|
75
|
+
}
|
|
76
|
+
source?: ChatEventSource
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type HostTargetEvent = {
|
|
80
|
+
type: "HOSTTARGET"
|
|
81
|
+
channel: string
|
|
82
|
+
source?: ChatEventSource
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type NoticeEvent = {
|
|
86
|
+
type: "NOTICE"
|
|
87
|
+
channel: string
|
|
88
|
+
tags: {
|
|
89
|
+
msgId: string
|
|
90
|
+
targetUserId: string
|
|
91
|
+
}
|
|
92
|
+
source?: ChatEventSource
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type RoomStateEvent = {
|
|
96
|
+
type: "ROOMSTATE"
|
|
97
|
+
channel: string
|
|
98
|
+
tags: {
|
|
99
|
+
roomId: string
|
|
100
|
+
slow: number
|
|
101
|
+
emoteOnly: boolean
|
|
102
|
+
followersOnly: boolean
|
|
103
|
+
subsOnly: boolean
|
|
104
|
+
}
|
|
105
|
+
source?: ChatEventSource
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type UserStateEvent = {
|
|
109
|
+
type: "USERSTATE"
|
|
110
|
+
channel: string
|
|
111
|
+
tags: {
|
|
112
|
+
id: string
|
|
113
|
+
userId: string
|
|
114
|
+
userType: TagUserType
|
|
115
|
+
displayName?: string
|
|
116
|
+
color?: string
|
|
117
|
+
badges?: TagBadges
|
|
118
|
+
badgeInfo?: TagBadgeInfo
|
|
119
|
+
emoteSets?: string[]
|
|
120
|
+
|
|
121
|
+
mod: boolean
|
|
122
|
+
subscriber: boolean
|
|
123
|
+
vip: boolean
|
|
124
|
+
turbo: boolean
|
|
125
|
+
}
|
|
126
|
+
source?: ChatEventSource
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type ClearMessageEvent = {
|
|
130
|
+
type: "CLEARMSG"
|
|
131
|
+
channel: string
|
|
132
|
+
tags: {
|
|
133
|
+
login: string
|
|
134
|
+
roomId: string
|
|
135
|
+
targetMsgId?: string
|
|
136
|
+
}
|
|
137
|
+
source?: ChatEventSource
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export type UserNoticeEvent = {
|
|
141
|
+
type: "USERNOTICE"
|
|
142
|
+
channel: string
|
|
143
|
+
tags: {
|
|
144
|
+
id: string
|
|
145
|
+
userId: string
|
|
146
|
+
userType: TagUserType
|
|
147
|
+
displayName?: string
|
|
148
|
+
login: string
|
|
149
|
+
color?: string
|
|
150
|
+
badges?: TagBadges
|
|
151
|
+
badgeInfo?: TagBadgeInfo
|
|
152
|
+
emotes?: TagEmotes
|
|
153
|
+
roomId: string
|
|
154
|
+
systemMsg: string
|
|
155
|
+
msgId:
|
|
156
|
+
| "sub"
|
|
157
|
+
| "resub"
|
|
158
|
+
| "subgift"
|
|
159
|
+
| "submysterygift"
|
|
160
|
+
| "giftpaidupgrade"
|
|
161
|
+
| "rewardgift"
|
|
162
|
+
| "anongiftpaidupgrade"
|
|
163
|
+
| "raid"
|
|
164
|
+
| "unraid"
|
|
165
|
+
| "ritual"
|
|
166
|
+
| "bitsbadgetier"
|
|
167
|
+
|
|
168
|
+
mod: boolean
|
|
169
|
+
subscriber: boolean
|
|
170
|
+
vip: boolean
|
|
171
|
+
turbo: boolean
|
|
172
|
+
}
|
|
173
|
+
source?: ChatEventSource
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export type ChatMessageEvent = {
|
|
177
|
+
type: "PRIVMSG"
|
|
178
|
+
channel: string
|
|
179
|
+
message: string
|
|
180
|
+
tags: {
|
|
181
|
+
id: string
|
|
182
|
+
userId: string
|
|
183
|
+
userType: TagUserType
|
|
184
|
+
displayName?: string
|
|
185
|
+
color?: string
|
|
186
|
+
badges?: TagBadges
|
|
187
|
+
badgeInfo?: TagBadgeInfo
|
|
188
|
+
emotes?: TagEmotes
|
|
189
|
+
roomId: string
|
|
190
|
+
|
|
191
|
+
firstMsg: boolean
|
|
192
|
+
mod: boolean
|
|
193
|
+
subscriber: boolean
|
|
194
|
+
vip: boolean
|
|
195
|
+
turbo: boolean
|
|
196
|
+
} & AllOrNothing<{
|
|
197
|
+
replyThreadParentMsgId: string
|
|
198
|
+
replyThreadParentUserLogin: string
|
|
199
|
+
replyParentMsgId: string
|
|
200
|
+
replyParentUserId: string
|
|
201
|
+
replyParentUserLogin: string
|
|
202
|
+
replyParentDisplayName: string
|
|
203
|
+
replyParentMsgBody: string
|
|
204
|
+
}> &
|
|
205
|
+
AllOrNothing<{
|
|
206
|
+
pinnedChatPaidIsSystemMessage: boolean
|
|
207
|
+
pinnedChatPaidLevel: string
|
|
208
|
+
pinnedChatPaidAmount: number
|
|
209
|
+
pinnedChatPaidExponent: number
|
|
210
|
+
pinnedChatPaidCurrency: string
|
|
211
|
+
}>
|
|
212
|
+
source?: ChatEventSource
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export type WhisperMessageEvent = {
|
|
216
|
+
type: "WHISPER"
|
|
217
|
+
channel: string
|
|
218
|
+
message: string
|
|
219
|
+
tags: {
|
|
220
|
+
messageId: string
|
|
221
|
+
threadId: string
|
|
222
|
+
userId: string
|
|
223
|
+
userType: TagUserType
|
|
224
|
+
displayName?: string
|
|
225
|
+
color?: string
|
|
226
|
+
badges?: TagBadges
|
|
227
|
+
emotes?: TagEmotes
|
|
228
|
+
turbo: boolean
|
|
229
|
+
}
|
|
230
|
+
source?: ChatEventSource
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export type ChatCommandEvent = {
|
|
234
|
+
type: "PRIVMSG-COMMAND"
|
|
235
|
+
channel: string
|
|
236
|
+
command: string
|
|
237
|
+
parameters?: string
|
|
238
|
+
tags: ChatMessageEvent["tags"]
|
|
239
|
+
source?: ChatEventSource
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export type WhisperCommandEvent = {
|
|
243
|
+
type: "WHISPER-COMMAND"
|
|
244
|
+
channel: string
|
|
245
|
+
command: string
|
|
246
|
+
parameters?: string
|
|
247
|
+
tags: WhisperMessageEvent["tags"]
|
|
248
|
+
source?: ChatEventSource
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export type ChatEvent =
|
|
252
|
+
| PingEvent
|
|
253
|
+
| WelcomeEvent
|
|
254
|
+
| CapabilitiesEvent
|
|
255
|
+
| JoinEvent
|
|
256
|
+
| PartEvent
|
|
257
|
+
| GlobalUserStateEvent
|
|
258
|
+
| ReconnectEvent
|
|
259
|
+
| ClearChatEvent
|
|
260
|
+
| HostTargetEvent
|
|
261
|
+
| NoticeEvent
|
|
262
|
+
| RoomStateEvent
|
|
263
|
+
| UserStateEvent
|
|
264
|
+
| ClearMessageEvent
|
|
265
|
+
| UserNoticeEvent
|
|
266
|
+
| ChatMessageEvent
|
|
267
|
+
| WhisperMessageEvent
|
|
268
|
+
| ChatCommandEvent
|
|
269
|
+
| WhisperCommandEvent
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ChatEvent } from "./events.js"
|
|
2
|
+
|
|
3
|
+
export type { ChatEvent, ChatEventSource } from "./events.js"
|
|
4
|
+
|
|
5
|
+
export type TwitchChatEventType = ChatEvent["type"]
|
|
6
|
+
|
|
7
|
+
export type TwitchChatEvent<
|
|
8
|
+
Type extends TwitchChatEventType = TwitchChatEventType,
|
|
9
|
+
> = Extract<ChatEvent, { type: Type }>
|