@overlaysymphony/twitch 0.1.0 → 0.2.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.
Files changed (75) hide show
  1. package/README.md +1 -1
  2. package/package.json +21 -22
  3. package/src/authentication/authentication.ts +85 -55
  4. package/src/chat/chat.ts +9 -8
  5. package/src/chat/interfaces/events.ts +29 -29
  6. package/src/chat/interfaces/index.ts +1 -1
  7. package/src/chat/parser.ts +9 -20
  8. package/src/eventsub/events/channel.ad_break.begin.ts +1 -1
  9. package/src/eventsub/events/channel.ban.ts +1 -1
  10. package/src/eventsub/events/channel.channel_points_custom_reward.add.ts +2 -2
  11. package/src/eventsub/events/channel.channel_points_custom_reward.remove.ts +2 -2
  12. package/src/eventsub/events/channel.channel_points_custom_reward.update.ts +2 -2
  13. package/src/eventsub/events/channel.channel_points_custom_reward_redemption.add.ts +2 -2
  14. package/src/eventsub/events/channel.channel_points_custom_reward_redemption.update.ts +2 -2
  15. package/src/eventsub/events/channel.charity_campaign.donate.ts +1 -1
  16. package/src/eventsub/events/channel.charity_campaign.progress.ts +1 -1
  17. package/src/eventsub/events/channel.charity_campaign.start.ts +1 -1
  18. package/src/eventsub/events/channel.charity_campaign.stop.ts +1 -1
  19. package/src/eventsub/events/channel.chat.clear.ts +1 -1
  20. package/src/eventsub/events/channel.chat.clear_user_messages.ts +1 -1
  21. package/src/eventsub/events/channel.chat.message_delete.ts +1 -1
  22. package/src/eventsub/events/channel.chat.notification.ts +1 -1
  23. package/src/eventsub/events/channel.cheer.ts +1 -1
  24. package/src/eventsub/events/channel.follow.ts +1 -1
  25. package/src/eventsub/events/channel.goal.begin.ts +1 -1
  26. package/src/eventsub/events/channel.goal.end.ts +1 -1
  27. package/src/eventsub/events/channel.goal.progress.ts +1 -1
  28. package/src/eventsub/events/channel.guest_star_guest.update.ts +1 -1
  29. package/src/eventsub/events/channel.guest_star_session.begin.ts +1 -1
  30. package/src/eventsub/events/channel.guest_star_session.end.ts +1 -1
  31. package/src/eventsub/events/channel.guest_star_settings.update.ts +1 -1
  32. package/src/eventsub/events/channel.hype_train.begin.ts +2 -2
  33. package/src/eventsub/events/channel.hype_train.end.ts +2 -2
  34. package/src/eventsub/events/channel.hype_train.progress.ts +2 -2
  35. package/src/eventsub/events/channel.moderator.add.ts +1 -1
  36. package/src/eventsub/events/channel.moderator.remove.ts +1 -1
  37. package/src/eventsub/events/channel.poll.begin.ts +2 -2
  38. package/src/eventsub/events/channel.poll.end.ts +2 -2
  39. package/src/eventsub/events/channel.poll.progress.ts +2 -2
  40. package/src/eventsub/events/channel.prediction.begin.ts +2 -2
  41. package/src/eventsub/events/channel.prediction.end.ts +2 -2
  42. package/src/eventsub/events/channel.prediction.lock.ts +2 -2
  43. package/src/eventsub/events/channel.prediction.progress.ts +2 -2
  44. package/src/eventsub/events/channel.raid.ts +1 -1
  45. package/src/eventsub/events/channel.shield_mode.begin.ts +1 -1
  46. package/src/eventsub/events/channel.shield_mode.end.ts +1 -1
  47. package/src/eventsub/events/channel.shoutout.create.ts +1 -1
  48. package/src/eventsub/events/channel.shoutout.receive.ts +1 -1
  49. package/src/eventsub/events/channel.subscribe.ts +1 -1
  50. package/src/eventsub/events/channel.subscription.end.ts +1 -1
  51. package/src/eventsub/events/channel.subscription.gift.ts +1 -1
  52. package/src/eventsub/events/channel.subscription.message.ts +1 -1
  53. package/src/eventsub/events/channel.unban.ts +1 -1
  54. package/src/eventsub/events/channel.update.ts +1 -1
  55. package/src/eventsub/events/index.ts +52 -51
  56. package/src/eventsub/events/stream.offline.ts +1 -1
  57. package/src/eventsub/events/stream.online.ts +1 -1
  58. package/src/eventsub/events/user.update.ts +1 -1
  59. package/src/eventsub/events-helpers.ts +2 -2
  60. package/src/eventsub/eventsub.ts +9 -9
  61. package/src/eventsub/messages.ts +1 -1
  62. package/src/helix/channel-points/custom-rewards.ts +2 -3
  63. package/src/helix/helix.ts +1 -1
  64. package/src/helix/subscriptions/subscriptions.ts +9 -19
  65. package/src/helix/users/users.ts +1 -1
  66. package/src/helpers/alerts/alerts.ts +3 -3
  67. package/src/helpers/charity/charity.ts +1 -1
  68. package/src/helpers/goal/goal.ts +1 -1
  69. package/src/helpers/hype-train/hype-train.ts +1 -1
  70. package/src/helpers/poll/poll.ts +1 -1
  71. package/src/helpers/prediction/prediction.ts +1 -1
  72. package/src/helpers/redemption/redemption.ts +1 -1
  73. package/src/helpers/status/status.ts +1 -1
  74. package/src/ui/authentication.ts +29 -85
  75. package/src/ui/popup.ts +45 -6
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # `@overlaysymphony/obs`
1
+ # `@overlaysymphony/twitch`
2
2
 
3
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
4
 
package/package.json CHANGED
@@ -1,40 +1,39 @@
1
1
  {
2
2
  "name": "@overlaysymphony/twitch",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Twitch module for the OverlaySymphony interactive streaming framework.",
5
5
  "homepage": "https://github.com/OverlaySymphony/overlaysymphony",
6
6
  "type": "module",
7
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",
8
+ "./authentication": "./src/authentication/index.ts",
9
+ "./chat": "./src/chat/index.ts",
10
+ "./eventsub": "./src/eventsub/index.ts",
11
+ "./helix/*": "./src/helix/*/index.ts",
12
+ "./helpers/*": "./src/helpers/*/index.ts",
13
+ "./ui/*": "./src/ui/*.ts",
14
14
  "./package.json": "./package.json"
15
15
  },
16
+ "dependencies": {
17
+ "@overlaysymphony/core": "0.1.0"
18
+ },
19
+ "devDependencies": {
20
+ "@vitest/coverage-v8": "^2.1.2",
21
+ "depcheck": "^1.4.7",
22
+ "eslint": "^9.19.0",
23
+ "prettier": "^3.4.2",
24
+ "typescript": "^5.7.2",
25
+ "vitest": "^2.1.2",
26
+ "@overlaysymphony/tooling": "0.0.0"
27
+ },
16
28
  "scripts": {
17
29
  "lint": "pnpm run \"/^lint-.*/\"",
18
30
  "lint-typecheck": "tsc --noEmit",
19
- "lint-eslint": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx .",
31
+ "lint-eslint": "eslint .",
20
32
  "lint-prettier": "prettier --check .",
21
33
  "lint-depcheck": "depcheck .",
22
34
  "test": "vitest",
23
35
  "dev": "echo TODO",
24
36
  "clean": "rm -rf node_modules/.cache tsconfig.tsbuildinfo dist",
25
37
  "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
38
  }
40
- }
39
+ }
@@ -1,6 +1,4 @@
1
- import querystring from "@overlaysymphony/core/libs/querystring"
2
-
3
- import { TwitchUser, getUsers } from "../helix/users/index.js"
1
+ import { type TwitchUser, getUsers } from "../helix/users/index.js"
4
2
 
5
3
  export interface Authentication {
6
4
  clientId: string
@@ -13,53 +11,97 @@ export interface Authentication {
13
11
 
14
12
  export type BareAuthentication = Omit<Authentication, "user">
15
13
 
16
- interface TAValidation {
17
- client_id: string
18
- login: string
19
- scope: string[]
20
- user_id: string
21
- expires_in: number
14
+ const localStorageKey = "overlaysymphony:service:twitch"
15
+
16
+ export function getCachedAuthentication(
17
+ scopes?: string[],
18
+ ): BareAuthentication | undefined {
19
+ const cache = localStorage.getItem(localStorageKey)
20
+ if (!cache) {
21
+ return undefined
22
+ }
23
+
24
+ const authentication = JSON.parse(cache) as BareAuthentication
25
+ authentication.expires = new Date(authentication.expires)
26
+
27
+ if (scopes) {
28
+ for (const scope of scopes) {
29
+ if (!authentication.scope.includes(scope)) {
30
+ localStorage.removeItem(localStorageKey)
31
+ return undefined
32
+ }
33
+ }
34
+ }
35
+
36
+ return authentication
22
37
  }
23
38
 
24
- interface TAResult {
25
- token_type: "bearer"
26
- access_token: string
27
- scope: string
39
+ export function setCachedAuthentication(
40
+ authentication: BareAuthentication,
41
+ ): void {
42
+ localStorage.setItem(localStorageKey, JSON.stringify(authentication))
28
43
  }
29
44
 
30
- const localStorageKey = "overlaysymphony:service:twitch"
45
+ export function clearCachedAuthentication(): void {
46
+ localStorage.removeItem(localStorageKey)
47
+ }
31
48
 
32
- export async function getAuthentication(): Promise<Authentication> {
33
- const raw = localStorage.getItem(localStorageKey)
34
- if (!raw) {
35
- throw new Error("Twitch authentication missing!")
49
+ export async function getAuthentication(
50
+ scopes?: string[],
51
+ ): Promise<Authentication | undefined> {
52
+ let authentication = getCachedAuthentication(scopes)
53
+ if (!authentication) {
54
+ return undefined
36
55
  }
37
56
 
38
- const cached = JSON.parse(raw) as BareAuthentication
39
- cached.expires = new Date(cached.expires)
57
+ try {
58
+ authentication = await validateAuthentication(authentication)
59
+ } catch (error) {
60
+ clearCachedAuthentication()
61
+ return undefined
62
+ }
40
63
 
41
- const [user] = await getUsers(cached as Authentication)
64
+ const [user] = await getUsers(authentication as Authentication)
42
65
 
43
- const authentication: Authentication = {
44
- ...cached,
66
+ return {
67
+ ...authentication,
45
68
  user,
46
69
  }
47
-
48
- return authentication
49
70
  }
50
71
 
51
- export function initiateAuthentication(
72
+ export async function popupAuthentication(
52
73
  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
- })}`
74
+ scopes: string[],
75
+ popupUrl: string,
76
+ ): Promise<void> {
77
+ const url = new URL(
78
+ `${popupUrl}?scopes=${scopes.join("+")}&clientId=${clientId}`,
79
+ )
80
+
81
+ await new Promise<void>((resolve) => {
82
+ const listener = (event: MessageEvent) => {
83
+ if (event.origin !== url.origin) return
84
+
85
+ // const source = (event.source as Window | null)?.name
86
+ // if (source !== "OverlaySymphonyTwitchAuthenticationPopup") return
87
+
88
+ const { type, authentication } = event.data
89
+ if (type !== "authentication") return
90
+
91
+ window.removeEventListener("message", listener)
92
+
93
+ setCachedAuthentication(authentication as BareAuthentication)
94
+ resolve()
95
+ }
96
+
97
+ window.addEventListener("message", listener)
98
+
99
+ window.open(
100
+ url,
101
+ "OverlaySymphonyTwitchAuthenticationPopup",
102
+ "width=520,height=840",
103
+ )
104
+ })
63
105
  }
64
106
 
65
107
  export async function validateAuthentication(
@@ -76,7 +118,13 @@ export async function validateAuthentication(
76
118
  throw new Error(await response.text())
77
119
  }
78
120
 
79
- const validated = (await response.json()) as TAValidation
121
+ const validated = (await response.json()) as {
122
+ client_id: string
123
+ login: string
124
+ scope: string[]
125
+ user_id: string
126
+ expires_in: number
127
+ }
80
128
 
81
129
  if (validated.client_id !== authentication.clientId) {
82
130
  throw new Error("ClientId mismatch.")
@@ -87,21 +135,3 @@ export async function validateAuthentication(
87
135
  expires: new Date(Date.now() + validated.expires_in * 1000),
88
136
  }
89
137
  }
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
- }
package/src/chat/chat.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import createDefer from "@overlaysymphony/core/libs/defer"
2
2
  import createPubSub from "@overlaysymphony/core/libs/pubsub"
3
3
 
4
- import { Authentication } from "../authentication/index.js"
4
+ import { type Authentication } from "../authentication/index.js"
5
5
 
6
- import { TwitchChatEvent, TwitchChatEventType } from "./interfaces/index.js"
6
+ import {
7
+ type TwitchChatEvent,
8
+ type TwitchChatEventType,
9
+ } from "./interfaces/index.js"
7
10
  import parseCommand from "./parser.js"
8
11
 
9
12
  type ChatListener = (callback: (event: TwitchChatEvent) => void) => () => void
@@ -22,7 +25,7 @@ type ChatMessageSubscriber = (
22
25
  callback: (event: TwitchChatEvent<"PRIVMSG">) => void,
23
26
  ) => () => void
24
27
 
25
- type ChatCommandSubscriber = {
28
+ interface ChatCommandSubscriber {
26
29
  (
27
30
  callback: (event: TwitchChatEvent<"PRIVMSG-COMMAND">) => void,
28
31
  name?: never,
@@ -67,8 +70,7 @@ export async function createChat(
67
70
  command.type === "JOIN" ||
68
71
  command.type === "USERSTATE" ||
69
72
  command.type === "HOSTTARGET" ||
70
- command.type === "NOTICE" ||
71
- false
73
+ command.type === "NOTICE"
72
74
  ) {
73
75
  continue
74
76
  }
@@ -106,16 +108,15 @@ export async function createChat(
106
108
  return promise.then(() => {
107
109
  const listen: ChatListener = (callback) => {
108
110
  return pubsub.subscribe((event) => {
109
- // @ts-ignore
110
111
  callback(event)
111
112
  })
112
113
  }
113
114
 
114
115
  const subscribe: ChatSubscriber = (types, callback) => {
115
116
  return pubsub.subscribe((event) => {
116
- // @ts-ignore
117
+ // @ts-expect-error: generic events are complicated
117
118
  if (types.includes(event.type)) {
118
- // @ts-ignore
119
+ // @ts-expect-error: generic events are complicated
119
120
  callback(event)
120
121
  }
121
122
  })
@@ -13,20 +13,20 @@ type TagEmotes = Record<
13
13
  Array<[startPosition: string, endPosition: string]>
14
14
  >
15
15
 
16
- export type PingEvent = {
16
+ export interface PingEvent {
17
17
  type: "PING"
18
18
  message: string
19
19
  source?: ChatEventSource
20
20
  }
21
21
 
22
- export type WelcomeEvent = {
22
+ export interface WelcomeEvent {
23
23
  type: "001" // Logged in (successfully authenticated)
24
24
  channel: string
25
25
  message: string
26
26
  source?: ChatEventSource
27
27
  }
28
28
 
29
- export type CapabilitiesEvent = {
29
+ export interface CapabilitiesEvent {
30
30
  type: "CAP"
31
31
  enabled: boolean
32
32
  nickname: string
@@ -34,21 +34,21 @@ export type CapabilitiesEvent = {
34
34
  source?: ChatEventSource
35
35
  }
36
36
 
37
- export type JoinEvent = {
37
+ export interface JoinEvent {
38
38
  type: "JOIN"
39
39
  channel: string
40
40
  source?: ChatEventSource
41
41
  }
42
42
 
43
- export type PartEvent = {
43
+ export interface PartEvent {
44
44
  type: "PART"
45
45
  channel: string
46
46
  source?: ChatEventSource
47
47
  }
48
48
 
49
- export type GlobalUserStateEvent = {
49
+ export interface GlobalUserStateEvent {
50
50
  type: "GLOBALUSERSTATE"
51
- tags: {
51
+ tags?: {
52
52
  userId: string
53
53
  userType: TagUserType
54
54
  displayName?: string
@@ -60,15 +60,15 @@ export type GlobalUserStateEvent = {
60
60
  source?: ChatEventSource
61
61
  }
62
62
 
63
- export type ReconnectEvent = {
63
+ export interface ReconnectEvent {
64
64
  type: "RECONNECT"
65
65
  source?: ChatEventSource
66
66
  }
67
67
 
68
- export type ClearChatEvent = {
68
+ export interface ClearChatEvent {
69
69
  type: "CLEARCHAT"
70
70
  channel: string
71
- tags: {
71
+ tags?: {
72
72
  roomId: string
73
73
  targetUserId: string
74
74
  banDuration: number
@@ -76,26 +76,26 @@ export type ClearChatEvent = {
76
76
  source?: ChatEventSource
77
77
  }
78
78
 
79
- export type HostTargetEvent = {
79
+ export interface HostTargetEvent {
80
80
  type: "HOSTTARGET"
81
81
  channel: string
82
82
  source?: ChatEventSource
83
83
  }
84
84
 
85
- export type NoticeEvent = {
85
+ export interface NoticeEvent {
86
86
  type: "NOTICE"
87
87
  channel: string
88
- tags: {
88
+ tags?: {
89
89
  msgId: string
90
90
  targetUserId: string
91
91
  }
92
92
  source?: ChatEventSource
93
93
  }
94
94
 
95
- export type RoomStateEvent = {
95
+ export interface RoomStateEvent {
96
96
  type: "ROOMSTATE"
97
97
  channel: string
98
- tags: {
98
+ tags?: {
99
99
  roomId: string
100
100
  slow: number
101
101
  emoteOnly: boolean
@@ -105,10 +105,10 @@ export type RoomStateEvent = {
105
105
  source?: ChatEventSource
106
106
  }
107
107
 
108
- export type UserStateEvent = {
108
+ export interface UserStateEvent {
109
109
  type: "USERSTATE"
110
110
  channel: string
111
- tags: {
111
+ tags?: {
112
112
  id: string
113
113
  userId: string
114
114
  userType: TagUserType
@@ -126,10 +126,10 @@ export type UserStateEvent = {
126
126
  source?: ChatEventSource
127
127
  }
128
128
 
129
- export type ClearMessageEvent = {
129
+ export interface ClearMessageEvent {
130
130
  type: "CLEARMSG"
131
131
  channel: string
132
- tags: {
132
+ tags?: {
133
133
  login: string
134
134
  roomId: string
135
135
  targetMsgId?: string
@@ -137,10 +137,10 @@ export type ClearMessageEvent = {
137
137
  source?: ChatEventSource
138
138
  }
139
139
 
140
- export type UserNoticeEvent = {
140
+ export interface UserNoticeEvent {
141
141
  type: "USERNOTICE"
142
142
  channel: string
143
- tags: {
143
+ tags?: {
144
144
  id: string
145
145
  userId: string
146
146
  userType: TagUserType
@@ -173,11 +173,11 @@ export type UserNoticeEvent = {
173
173
  source?: ChatEventSource
174
174
  }
175
175
 
176
- export type ChatMessageEvent = {
176
+ export interface ChatMessageEvent {
177
177
  type: "PRIVMSG"
178
178
  channel: string
179
179
  message: string
180
- tags: {
180
+ tags?: {
181
181
  id: string
182
182
  userId: string
183
183
  userType: TagUserType
@@ -212,11 +212,11 @@ export type ChatMessageEvent = {
212
212
  source?: ChatEventSource
213
213
  }
214
214
 
215
- export type WhisperMessageEvent = {
215
+ export interface WhisperMessageEvent {
216
216
  type: "WHISPER"
217
217
  channel: string
218
218
  message: string
219
- tags: {
219
+ tags?: {
220
220
  messageId: string
221
221
  threadId: string
222
222
  userId: string
@@ -230,21 +230,21 @@ export type WhisperMessageEvent = {
230
230
  source?: ChatEventSource
231
231
  }
232
232
 
233
- export type ChatCommandEvent = {
233
+ export interface ChatCommandEvent {
234
234
  type: "PRIVMSG-COMMAND"
235
235
  channel: string
236
236
  command: string
237
237
  parameters?: string
238
- tags: ChatMessageEvent["tags"]
238
+ tags?: ChatMessageEvent["tags"]
239
239
  source?: ChatEventSource
240
240
  }
241
241
 
242
- export type WhisperCommandEvent = {
242
+ export interface WhisperCommandEvent {
243
243
  type: "WHISPER-COMMAND"
244
244
  channel: string
245
245
  command: string
246
246
  parameters?: string
247
- tags: WhisperMessageEvent["tags"]
247
+ tags?: WhisperMessageEvent["tags"]
248
248
  source?: ChatEventSource
249
249
  }
250
250
 
@@ -1,4 +1,4 @@
1
- import { ChatEvent } from "./events.js"
1
+ import { type ChatEvent } from "./events.js"
2
2
 
3
3
  export type { ChatEvent, ChatEventSource } from "./events.js"
4
4
 
@@ -1,4 +1,4 @@
1
- import { ChatEvent, ChatEventSource } from "./interfaces/index.js"
1
+ import { type ChatEvent, type ChatEventSource } from "./interfaces/index.js"
2
2
 
3
3
  // Parses an IRC message and returns a JSON object with the message's
4
4
  // component parts (tags, source (nick and host), command, parameters).
@@ -30,7 +30,7 @@ export default function parseEvent(input: string): ChatEvent | undefined {
30
30
 
31
31
  // core
32
32
  try {
33
- const hasParameters = input.indexOf(":", idx) > -1
33
+ const hasParameters = input.includes(":", idx)
34
34
  if (hasParameters) {
35
35
  const endIdx = input.indexOf(":", idx)
36
36
 
@@ -42,7 +42,7 @@ export default function parseEvent(input: string): ChatEvent | undefined {
42
42
  return {
43
43
  ...message,
44
44
  source,
45
- // @ts-ignore
45
+ // @ts-expect-error: generic objects are complicated
46
46
  tags,
47
47
  }
48
48
  } else {
@@ -51,11 +51,11 @@ export default function parseEvent(input: string): ChatEvent | undefined {
51
51
  return {
52
52
  ...message,
53
53
  source,
54
- // @ts-ignore
54
+ // @ts-expect-error: generic objects are complicated
55
55
  tags,
56
56
  }
57
57
  }
58
- } catch (e) {
58
+ } catch (error) {
59
59
  return undefined
60
60
  }
61
61
  }
@@ -129,8 +129,7 @@ function parseTags(input: string): Record<string, unknown> {
129
129
  key === "banDuration" ||
130
130
  key === "pinnedChatPaidAmount" ||
131
131
  key === "pinnedChatPaidExponent" ||
132
- key === "slow" ||
133
- false
132
+ key === "slow"
134
133
  ) {
135
134
  tags[key] = parseInt(value)
136
135
  continue
@@ -145,8 +144,7 @@ function parseTags(input: string): Record<string, unknown> {
145
144
  key === "emoteOnly" ||
146
145
  key === "followersOnly" ||
147
146
  key === "subsOnly" ||
148
- key === "pinnedChatPaidIsSystemMessage" ||
149
- false
147
+ key === "pinnedChatPaidIsSystemMessage"
150
148
  ) {
151
149
  tags[key] = !!parseInt(value)
152
150
  continue
@@ -163,14 +161,12 @@ function parseCore(inputEvent: string, parameters: string): ChatEvent {
163
161
 
164
162
  switch (type) {
165
163
  case "PING":
166
- // @ts-ignore
167
164
  return {
168
165
  type,
169
166
  message: parameters,
170
167
  }
171
168
 
172
169
  case "001":
173
- // @ts-ignore
174
170
  return {
175
171
  type,
176
172
  channel: parts[0],
@@ -178,7 +174,6 @@ function parseCore(inputEvent: string, parameters: string): ChatEvent {
178
174
  }
179
175
 
180
176
  case "CAP":
181
- // @ts-ignore
182
177
  return {
183
178
  type,
184
179
  enabled: parts[1] === "ACK",
@@ -188,7 +183,6 @@ function parseCore(inputEvent: string, parameters: string): ChatEvent {
188
183
 
189
184
  case "JOIN":
190
185
  case "PART":
191
- // @ts-ignore
192
186
  return {
193
187
  type,
194
188
  channel: parts[0],
@@ -196,7 +190,6 @@ function parseCore(inputEvent: string, parameters: string): ChatEvent {
196
190
 
197
191
  case "GLOBALUSERSTATE":
198
192
  case "RECONNECT":
199
- // @ts-ignore
200
193
  return {
201
194
  type,
202
195
  }
@@ -206,7 +199,6 @@ function parseCore(inputEvent: string, parameters: string): ChatEvent {
206
199
  case "NOTICE":
207
200
  case "ROOMSTATE":
208
201
  case "USERSTATE":
209
- // @ts-ignore
210
202
  return {
211
203
  type,
212
204
  channel: parts[0],
@@ -214,7 +206,6 @@ function parseCore(inputEvent: string, parameters: string): ChatEvent {
214
206
 
215
207
  case "CLEARMSG":
216
208
  case "USERNOTICE":
217
- // @ts-ignore
218
209
  return {
219
210
  type,
220
211
  channel: parts[0],
@@ -222,10 +213,9 @@ function parseCore(inputEvent: string, parameters: string): ChatEvent {
222
213
 
223
214
  case "PRIVMSG":
224
215
  case "WHISPER":
225
- if (parameters[0] === "!") {
216
+ if (parameters.startsWith("!")) {
226
217
  const index = parameters.indexOf(" ")
227
218
 
228
- // @ts-ignore
229
219
  return {
230
220
  type: `${type}-COMMAND`,
231
221
  channel: parts[0],
@@ -233,7 +223,6 @@ function parseCore(inputEvent: string, parameters: string): ChatEvent {
233
223
  parameters: index > -1 ? parameters.slice(index + 1) : undefined,
234
224
  }
235
225
  } else {
236
- // @ts-ignore
237
226
  return {
238
227
  type,
239
228
  channel: parts[0],
@@ -261,5 +250,5 @@ function parseCore(inputEvent: string, parameters: string): ChatEvent {
261
250
  }
262
251
 
263
252
  function toCamelCase(dashedCase: string): string {
264
- return dashedCase.replace(/[-:]([a-z])/g, (_, b) => `${b.toUpperCase()}`)
253
+ return dashedCase.replace(/[-:]([a-z])/g, (_, b: string) => b.toUpperCase())
265
254
  }
@@ -1,4 +1,4 @@
1
- import { BaseSubscription } from "../events-helpers.js"
1
+ import { type BaseSubscription } from "../events-helpers.js"
2
2
 
3
3
  type ChannelAdBreakBeginType = "channel.ad_break.begin"
4
4
  type ChannelAdBreakBeginVersion = "beta"
@@ -1,4 +1,4 @@
1
- import { BaseSubscription } from "../events-helpers.js"
1
+ import { type BaseSubscription } from "../events-helpers.js"
2
2
 
3
3
  type ChannelBanType = "channel.ban"
4
4
  type ChannelBanVersion = "1"
@@ -1,6 +1,6 @@
1
- import { BaseSubscription } from "../events-helpers.js"
1
+ import { type BaseSubscription } from "../events-helpers.js"
2
2
 
3
- import { ChannelPointsCustomRewardGlobalCooldown, ChannelPointsCustomRewardImage, ChannelPointsCustomRewardMaxPerStream, ChannelPointsCustomRewardMaxPerUserPerStream } from "./channel.channel_points_custom_reward._.js"
3
+ import { type ChannelPointsCustomRewardGlobalCooldown, type ChannelPointsCustomRewardImage, type ChannelPointsCustomRewardMaxPerStream, type ChannelPointsCustomRewardMaxPerUserPerStream } from "./channel.channel_points_custom_reward._.js"
4
4
 
5
5
  type ChannelPointsCustomRewardAddType = "channel.channel_points_custom_reward.add"
6
6
  type ChannelPointsCustomRewardAddVersion = "1"
@@ -1,6 +1,6 @@
1
- import { BaseSubscription } from "../events-helpers.js"
1
+ import { type BaseSubscription } from "../events-helpers.js"
2
2
 
3
- import { ChannelPointsCustomRewardGlobalCooldown, ChannelPointsCustomRewardImage, ChannelPointsCustomRewardMaxPerStream, ChannelPointsCustomRewardMaxPerUserPerStream } from "./channel.channel_points_custom_reward._.js"
3
+ import { type ChannelPointsCustomRewardGlobalCooldown, type ChannelPointsCustomRewardImage, type ChannelPointsCustomRewardMaxPerStream, type ChannelPointsCustomRewardMaxPerUserPerStream } from "./channel.channel_points_custom_reward._.js"
4
4
 
5
5
  type ChannelPointsCustomRewardRemoveType = "channel.channel_points_custom_reward.remove"
6
6
  type ChannelPointsCustomRewardRemoveVersion = "1"
@@ -1,6 +1,6 @@
1
- import { BaseSubscription } from "../events-helpers.js"
1
+ import { type BaseSubscription } from "../events-helpers.js"
2
2
 
3
- import { ChannelPointsCustomRewardGlobalCooldown, ChannelPointsCustomRewardImage, ChannelPointsCustomRewardMaxPerStream, ChannelPointsCustomRewardMaxPerUserPerStream } from "./channel.channel_points_custom_reward._.js"
3
+ import { type ChannelPointsCustomRewardGlobalCooldown, type ChannelPointsCustomRewardImage, type ChannelPointsCustomRewardMaxPerStream, type ChannelPointsCustomRewardMaxPerUserPerStream } from "./channel.channel_points_custom_reward._.js"
4
4
 
5
5
  type ChannelPointsCustomRewardUpdateType = "channel.channel_points_custom_reward.update"
6
6
  type ChannelPointsCustomRewardUpdateVersion = "1"