@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.
- package/README.md +1 -1
- package/package.json +21 -22
- package/src/authentication/authentication.ts +85 -55
- package/src/chat/chat.ts +9 -8
- package/src/chat/interfaces/events.ts +29 -29
- package/src/chat/interfaces/index.ts +1 -1
- package/src/chat/parser.ts +9 -20
- package/src/eventsub/events/channel.ad_break.begin.ts +1 -1
- package/src/eventsub/events/channel.ban.ts +1 -1
- package/src/eventsub/events/channel.channel_points_custom_reward.add.ts +2 -2
- package/src/eventsub/events/channel.channel_points_custom_reward.remove.ts +2 -2
- package/src/eventsub/events/channel.channel_points_custom_reward.update.ts +2 -2
- package/src/eventsub/events/channel.channel_points_custom_reward_redemption.add.ts +2 -2
- package/src/eventsub/events/channel.channel_points_custom_reward_redemption.update.ts +2 -2
- package/src/eventsub/events/channel.charity_campaign.donate.ts +1 -1
- package/src/eventsub/events/channel.charity_campaign.progress.ts +1 -1
- package/src/eventsub/events/channel.charity_campaign.start.ts +1 -1
- package/src/eventsub/events/channel.charity_campaign.stop.ts +1 -1
- package/src/eventsub/events/channel.chat.clear.ts +1 -1
- package/src/eventsub/events/channel.chat.clear_user_messages.ts +1 -1
- package/src/eventsub/events/channel.chat.message_delete.ts +1 -1
- package/src/eventsub/events/channel.chat.notification.ts +1 -1
- package/src/eventsub/events/channel.cheer.ts +1 -1
- package/src/eventsub/events/channel.follow.ts +1 -1
- package/src/eventsub/events/channel.goal.begin.ts +1 -1
- package/src/eventsub/events/channel.goal.end.ts +1 -1
- package/src/eventsub/events/channel.goal.progress.ts +1 -1
- package/src/eventsub/events/channel.guest_star_guest.update.ts +1 -1
- package/src/eventsub/events/channel.guest_star_session.begin.ts +1 -1
- package/src/eventsub/events/channel.guest_star_session.end.ts +1 -1
- package/src/eventsub/events/channel.guest_star_settings.update.ts +1 -1
- package/src/eventsub/events/channel.hype_train.begin.ts +2 -2
- package/src/eventsub/events/channel.hype_train.end.ts +2 -2
- package/src/eventsub/events/channel.hype_train.progress.ts +2 -2
- package/src/eventsub/events/channel.moderator.add.ts +1 -1
- package/src/eventsub/events/channel.moderator.remove.ts +1 -1
- package/src/eventsub/events/channel.poll.begin.ts +2 -2
- package/src/eventsub/events/channel.poll.end.ts +2 -2
- package/src/eventsub/events/channel.poll.progress.ts +2 -2
- package/src/eventsub/events/channel.prediction.begin.ts +2 -2
- package/src/eventsub/events/channel.prediction.end.ts +2 -2
- package/src/eventsub/events/channel.prediction.lock.ts +2 -2
- package/src/eventsub/events/channel.prediction.progress.ts +2 -2
- package/src/eventsub/events/channel.raid.ts +1 -1
- package/src/eventsub/events/channel.shield_mode.begin.ts +1 -1
- package/src/eventsub/events/channel.shield_mode.end.ts +1 -1
- package/src/eventsub/events/channel.shoutout.create.ts +1 -1
- package/src/eventsub/events/channel.shoutout.receive.ts +1 -1
- package/src/eventsub/events/channel.subscribe.ts +1 -1
- package/src/eventsub/events/channel.subscription.end.ts +1 -1
- package/src/eventsub/events/channel.subscription.gift.ts +1 -1
- package/src/eventsub/events/channel.subscription.message.ts +1 -1
- package/src/eventsub/events/channel.unban.ts +1 -1
- package/src/eventsub/events/channel.update.ts +1 -1
- package/src/eventsub/events/index.ts +52 -51
- package/src/eventsub/events/stream.offline.ts +1 -1
- package/src/eventsub/events/stream.online.ts +1 -1
- package/src/eventsub/events/user.update.ts +1 -1
- package/src/eventsub/events-helpers.ts +2 -2
- package/src/eventsub/eventsub.ts +9 -9
- package/src/eventsub/messages.ts +1 -1
- package/src/helix/channel-points/custom-rewards.ts +2 -3
- package/src/helix/helix.ts +1 -1
- package/src/helix/subscriptions/subscriptions.ts +9 -19
- package/src/helix/users/users.ts +1 -1
- package/src/helpers/alerts/alerts.ts +3 -3
- package/src/helpers/charity/charity.ts +1 -1
- package/src/helpers/goal/goal.ts +1 -1
- package/src/helpers/hype-train/hype-train.ts +1 -1
- package/src/helpers/poll/poll.ts +1 -1
- package/src/helpers/prediction/prediction.ts +1 -1
- package/src/helpers/redemption/redemption.ts +1 -1
- package/src/helpers/status/status.ts +1 -1
- package/src/ui/authentication.ts +29 -85
- package/src/ui/popup.ts +45 -6
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# `@overlaysymphony/
|
|
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.
|
|
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.
|
|
9
|
-
"./chat": "./src/chat/index.
|
|
10
|
-
"./eventsub": "./src/eventsub/index.
|
|
11
|
-
"./helix/*": "./src/helix/*/index.
|
|
12
|
-
"./helpers/*": "./src/helpers/*/index.
|
|
13
|
-
"./ui/*": "./src/ui/*.
|
|
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
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
39
|
+
export function setCachedAuthentication(
|
|
40
|
+
authentication: BareAuthentication,
|
|
41
|
+
): void {
|
|
42
|
+
localStorage.setItem(localStorageKey, JSON.stringify(authentication))
|
|
28
43
|
}
|
|
29
44
|
|
|
30
|
-
|
|
45
|
+
export function clearCachedAuthentication(): void {
|
|
46
|
+
localStorage.removeItem(localStorageKey)
|
|
47
|
+
}
|
|
31
48
|
|
|
32
|
-
export async function getAuthentication(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
39
|
-
|
|
57
|
+
try {
|
|
58
|
+
authentication = await validateAuthentication(authentication)
|
|
59
|
+
} catch (error) {
|
|
60
|
+
clearCachedAuthentication()
|
|
61
|
+
return undefined
|
|
62
|
+
}
|
|
40
63
|
|
|
41
|
-
const [user] = await getUsers(
|
|
64
|
+
const [user] = await getUsers(authentication as Authentication)
|
|
42
65
|
|
|
43
|
-
|
|
44
|
-
...
|
|
66
|
+
return {
|
|
67
|
+
...authentication,
|
|
45
68
|
user,
|
|
46
69
|
}
|
|
47
|
-
|
|
48
|
-
return authentication
|
|
49
70
|
}
|
|
50
71
|
|
|
51
|
-
export function
|
|
72
|
+
export async function popupAuthentication(
|
|
52
73
|
clientId: string,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
):
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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-
|
|
117
|
+
// @ts-expect-error: generic events are complicated
|
|
117
118
|
if (types.includes(event.type)) {
|
|
118
|
-
// @ts-
|
|
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
|
|
16
|
+
export interface PingEvent {
|
|
17
17
|
type: "PING"
|
|
18
18
|
message: string
|
|
19
19
|
source?: ChatEventSource
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export
|
|
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
|
|
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
|
|
37
|
+
export interface JoinEvent {
|
|
38
38
|
type: "JOIN"
|
|
39
39
|
channel: string
|
|
40
40
|
source?: ChatEventSource
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export
|
|
43
|
+
export interface PartEvent {
|
|
44
44
|
type: "PART"
|
|
45
45
|
channel: string
|
|
46
46
|
source?: ChatEventSource
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export
|
|
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
|
|
63
|
+
export interface ReconnectEvent {
|
|
64
64
|
type: "RECONNECT"
|
|
65
65
|
source?: ChatEventSource
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export
|
|
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
|
|
79
|
+
export interface HostTargetEvent {
|
|
80
80
|
type: "HOSTTARGET"
|
|
81
81
|
channel: string
|
|
82
82
|
source?: ChatEventSource
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
export
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
233
|
+
export interface ChatCommandEvent {
|
|
234
234
|
type: "PRIVMSG-COMMAND"
|
|
235
235
|
channel: string
|
|
236
236
|
command: string
|
|
237
237
|
parameters?: string
|
|
238
|
-
tags
|
|
238
|
+
tags?: ChatMessageEvent["tags"]
|
|
239
239
|
source?: ChatEventSource
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
export
|
|
242
|
+
export interface WhisperCommandEvent {
|
|
243
243
|
type: "WHISPER-COMMAND"
|
|
244
244
|
channel: string
|
|
245
245
|
command: string
|
|
246
246
|
parameters?: string
|
|
247
|
-
tags
|
|
247
|
+
tags?: WhisperMessageEvent["tags"]
|
|
248
248
|
source?: ChatEventSource
|
|
249
249
|
}
|
|
250
250
|
|
package/src/chat/parser.ts
CHANGED
|
@@ -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.
|
|
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-
|
|
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-
|
|
54
|
+
// @ts-expect-error: generic objects are complicated
|
|
55
55
|
tags,
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
} catch (
|
|
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
|
|
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) =>
|
|
253
|
+
return dashedCase.replace(/[-:]([a-z])/g, (_, b: string) => b.toUpperCase())
|
|
265
254
|
}
|
|
@@ -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"
|