multichat-ts 0.0.94 → 0.0.95
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/dist/twitch.d.ts +20 -3
- package/dist/twitch.js +53 -16
- package/package.json +1 -1
- package/src/default/twitch.ts +64 -16
package/dist/twitch.d.ts
CHANGED
|
@@ -6,24 +6,40 @@ type EventCallbackFunctions = {
|
|
|
6
6
|
delete_message: (data: DeleteMessage) => unknown;
|
|
7
7
|
event: (event: Event) => unknown;
|
|
8
8
|
raw_message: (message: IRC_Message) => unknown;
|
|
9
|
+
connected: () => unknown;
|
|
10
|
+
auth_error: () => unknown;
|
|
9
11
|
};
|
|
10
12
|
type EventNames = keyof EventCallbackFunctions;
|
|
11
13
|
export declare class TwitchIRC {
|
|
12
14
|
channel_name?: string;
|
|
15
|
+
auth?: {
|
|
16
|
+
username: string;
|
|
17
|
+
token: string;
|
|
18
|
+
} | undefined;
|
|
19
|
+
bot_name?: string;
|
|
20
|
+
bot_token?: string;
|
|
13
21
|
private assets;
|
|
14
22
|
latency: number;
|
|
15
23
|
private ping;
|
|
16
24
|
private public_listeners;
|
|
17
25
|
socket?: WebSocket;
|
|
18
|
-
|
|
19
|
-
constructor(
|
|
26
|
+
wsCustom?: WebSocket | undefined;
|
|
27
|
+
constructor(options: {
|
|
28
|
+
ws?: WebSocket;
|
|
29
|
+
auth?: {
|
|
30
|
+
username: string;
|
|
31
|
+
token: string;
|
|
32
|
+
};
|
|
33
|
+
});
|
|
20
34
|
setBadges(badges: BadgeURLsBySetIDOrSetIDAndVersion): void;
|
|
21
35
|
setExternalEmotes(external_emotes: EmoteURLsByName): void;
|
|
22
36
|
getStoredBadges(): BadgeURLsBySetIDOrSetIDAndVersion;
|
|
23
37
|
getStoredExternalEmotes(): EmoteURLsByName;
|
|
38
|
+
authenticate(username: string, token: string): void;
|
|
24
39
|
connect(channel?: {
|
|
25
40
|
channelName?: string;
|
|
26
41
|
}): void;
|
|
42
|
+
send(message: string, replyParentMessageId?: string): void;
|
|
27
43
|
disconnect(): void;
|
|
28
44
|
on<EventName extends EventNames>(event_name: EventName, callback_fn: EventCallbackFunctions[EventName]): void;
|
|
29
45
|
isConnected(): this is {
|
|
@@ -34,7 +50,7 @@ export declare class TwitchIRC {
|
|
|
34
50
|
private onOpen;
|
|
35
51
|
private onClose;
|
|
36
52
|
private sendPing;
|
|
37
|
-
private
|
|
53
|
+
private sendIRC;
|
|
38
54
|
private onMessage;
|
|
39
55
|
}
|
|
40
56
|
type IRC_Message = {
|
|
@@ -63,6 +79,7 @@ declare const RAW_TAGS: {
|
|
|
63
79
|
readonly PART: readonly [];
|
|
64
80
|
readonly PING: readonly [];
|
|
65
81
|
readonly PONG: readonly [];
|
|
82
|
+
readonly '001': readonly [];
|
|
66
83
|
readonly PRIVMSG: readonly ["badge-info", "badges", "bits", "color", "display-name", "emotes", "emote-only", "id", "mod", "custom-reward-id", "reply-thread-parent-display-name", "reply-thread-parent-user-id", "pinned-chat-paid-amount", "pinned-chat-paid-currency", "pinned-chat-paid-exponent", "pinned-chat-paid-level", "pinned-chat-paid-is-system-message", "reply-parent-msg-id", "reply-parent-user-id", "reply-parent-user-login", "reply-parent-display-name", "reply-parent-msg-body", "reply-thread-parent-msg-id", "reply-thread-parent-user-login", "room-id", "subscriber", "tmi-sent-ts", "turbo", "user-id", "user-type", "vip", "client-nonce", "first-msg", "flags", "returning-chatter"];
|
|
67
84
|
readonly RECONNECT: readonly [];
|
|
68
85
|
readonly ROOMSTATE: readonly ["emote-only", "followers-only", "r9k", "room-id", "slow", "subs-only"];
|
package/dist/twitch.js
CHANGED
|
@@ -5,6 +5,9 @@ const PING_INTERVAL_MS = 30_000;
|
|
|
5
5
|
const PING_TIMEOUT_MS = 10_000;
|
|
6
6
|
export class TwitchIRC {
|
|
7
7
|
channel_name;
|
|
8
|
+
auth;
|
|
9
|
+
bot_name;
|
|
10
|
+
bot_token;
|
|
8
11
|
assets = {
|
|
9
12
|
external_emotes: {},
|
|
10
13
|
badges: {},
|
|
@@ -13,9 +16,10 @@ export class TwitchIRC {
|
|
|
13
16
|
ping = {};
|
|
14
17
|
public_listeners = {};
|
|
15
18
|
socket;
|
|
16
|
-
|
|
17
|
-
constructor(
|
|
18
|
-
this.
|
|
19
|
+
wsCustom;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.wsCustom = options.ws;
|
|
22
|
+
this.auth = options.auth;
|
|
19
23
|
}
|
|
20
24
|
setBadges(badges) {
|
|
21
25
|
this.assets.badges = { ...badges, ...this.assets.badges };
|
|
@@ -29,6 +33,10 @@ export class TwitchIRC {
|
|
|
29
33
|
getStoredExternalEmotes() {
|
|
30
34
|
return this.assets.external_emotes;
|
|
31
35
|
}
|
|
36
|
+
authenticate(username, token) {
|
|
37
|
+
this.bot_name = username;
|
|
38
|
+
this.bot_token = token;
|
|
39
|
+
}
|
|
32
40
|
connect(channel) {
|
|
33
41
|
if (channel?.channelName)
|
|
34
42
|
this.channel_name = channel.channelName;
|
|
@@ -37,13 +45,21 @@ export class TwitchIRC {
|
|
|
37
45
|
console.log(`connecting to ${this.channel_name}...`);
|
|
38
46
|
this.socket?.close();
|
|
39
47
|
this.socket = new WebSocket('wss://irc-ws.chat.twitch.tv', null, {
|
|
40
|
-
WebSocket: this.
|
|
48
|
+
WebSocket: this.wsCustom,
|
|
41
49
|
});
|
|
42
50
|
this.socket.onopen = () => this.onOpen();
|
|
43
51
|
this.socket.onclose = () => this.onClose();
|
|
44
52
|
this.socket.onmessage = (event) => this.onMessage(event);
|
|
45
53
|
}
|
|
54
|
+
send(message, replyParentMessageId) {
|
|
55
|
+
if (this.auth === undefined)
|
|
56
|
+
return console.error('No Auth Information');
|
|
57
|
+
if (!this.isConnected())
|
|
58
|
+
return console.error('Not Connected');
|
|
59
|
+
this.socket.send(`${replyParentMessageId ? `@reply-parent-msg-id=${replyParentMessageId} ` : ''}PRIVMSG #${this.channel_name} :${message}`);
|
|
60
|
+
}
|
|
46
61
|
disconnect() {
|
|
62
|
+
this.socket?.send(`Part #${this.channel_name}`);
|
|
47
63
|
this.socket?.close();
|
|
48
64
|
}
|
|
49
65
|
on(event_name, callback_fn) {
|
|
@@ -53,11 +69,18 @@ export class TwitchIRC {
|
|
|
53
69
|
return !!this.socket && this.socket.readyState === WebSocket.OPEN;
|
|
54
70
|
}
|
|
55
71
|
onOpen() {
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
this.sendIRC('CAP REQ :twitch.tv/commands twitch.tv/tags');
|
|
73
|
+
if (this.auth !== undefined) {
|
|
74
|
+
this.sendIRC(`PASS oauth:${this.auth.token}`);
|
|
75
|
+
this.sendIRC(`NICK ${this.auth.username}`);
|
|
76
|
+
this.sendIRC(`JOIN #${this.channel_name}`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
this.sendIRC(`PASS ${ANONYMOUS_IRC_PASS}`);
|
|
80
|
+
this.sendIRC(`NICK ${ANONYMOUS_IRC_LOGIN}`);
|
|
81
|
+
this.sendIRC(`JOIN #${this.channel_name}`);
|
|
82
|
+
}
|
|
83
|
+
this.public_listeners.connected?.();
|
|
61
84
|
if (this.ping.interval)
|
|
62
85
|
clearInterval(this.ping.interval);
|
|
63
86
|
this.sendPing();
|
|
@@ -68,7 +91,7 @@ export class TwitchIRC {
|
|
|
68
91
|
clearTimeout(this.ping.timeout);
|
|
69
92
|
}
|
|
70
93
|
sendPing() {
|
|
71
|
-
this.
|
|
94
|
+
this.sendIRC('PING');
|
|
72
95
|
this.ping.lastSentTimestamp = Date.now();
|
|
73
96
|
if (this.ping.timeout)
|
|
74
97
|
clearTimeout(this.ping.timeout);
|
|
@@ -77,10 +100,9 @@ export class TwitchIRC {
|
|
|
77
100
|
this.connect();
|
|
78
101
|
}, PING_TIMEOUT_MS);
|
|
79
102
|
}
|
|
80
|
-
|
|
81
|
-
if (!this.isConnected())
|
|
82
|
-
|
|
83
|
-
}
|
|
103
|
+
sendIRC(irc_message) {
|
|
104
|
+
if (!this.isConnected())
|
|
105
|
+
return console.error('Not Connected');
|
|
84
106
|
this.socket?.send(irc_message);
|
|
85
107
|
}
|
|
86
108
|
onMessage(event) {
|
|
@@ -100,7 +122,21 @@ export class TwitchIRC {
|
|
|
100
122
|
break;
|
|
101
123
|
}
|
|
102
124
|
case 'PING': {
|
|
103
|
-
this.
|
|
125
|
+
this.sendIRC('PONG');
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case '001': {
|
|
129
|
+
console.log(`Connected to Twitch IRC as ${this.auth?.username ?? 'Anonymous'} (${this.channel_name})`);
|
|
130
|
+
this.public_listeners.connected?.();
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case 'NOTICE': {
|
|
134
|
+
if (message.params[0] === 'Login authentication failed' ||
|
|
135
|
+
message.params[0] === 'Improperly formatted auth') {
|
|
136
|
+
clearTimeout(this.ping.timeout);
|
|
137
|
+
this.disconnect();
|
|
138
|
+
this.public_listeners.auth_error?.();
|
|
139
|
+
}
|
|
104
140
|
break;
|
|
105
141
|
}
|
|
106
142
|
case 'CLEARCHAT': {
|
|
@@ -249,7 +285,7 @@ function parseIRCLine(line) {
|
|
|
249
285
|
const command = components[componentIndex];
|
|
250
286
|
componentIndex++;
|
|
251
287
|
let channel = '';
|
|
252
|
-
if (components[componentIndex]
|
|
288
|
+
if (components[componentIndex]?.startsWith('#'))
|
|
253
289
|
channel = components[componentIndex].slice(1);
|
|
254
290
|
componentIndex++;
|
|
255
291
|
const params = [];
|
|
@@ -317,6 +353,7 @@ const RAW_TAGS = {
|
|
|
317
353
|
PART: [],
|
|
318
354
|
PING: [],
|
|
319
355
|
PONG: [],
|
|
356
|
+
'001': [],
|
|
320
357
|
PRIVMSG: [
|
|
321
358
|
'badge-info',
|
|
322
359
|
'badges',
|
package/package.json
CHANGED
package/src/default/twitch.ts
CHANGED
|
@@ -21,12 +21,18 @@ type EventCallbackFunctions = {
|
|
|
21
21
|
delete_message: (data: DeleteMessage) => unknown;
|
|
22
22
|
event: (event: Event) => unknown;
|
|
23
23
|
raw_message: (message: IRC_Message) => unknown;
|
|
24
|
+
connected: () => unknown;
|
|
25
|
+
auth_error: () => unknown;
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
type EventNames = keyof EventCallbackFunctions;
|
|
27
29
|
|
|
28
30
|
export class TwitchIRC {
|
|
29
31
|
public channel_name?: string;
|
|
32
|
+
public auth?: { username: string; token: string } | undefined;
|
|
33
|
+
public bot_name?: string;
|
|
34
|
+
public bot_token?: string;
|
|
35
|
+
|
|
30
36
|
private assets: {
|
|
31
37
|
external_emotes: EmoteURLsByName;
|
|
32
38
|
badges: BadgeURLsBySetIDOrSetIDAndVersion;
|
|
@@ -45,10 +51,11 @@ export class TwitchIRC {
|
|
|
45
51
|
private public_listeners: Partial<EventCallbackFunctions> = {};
|
|
46
52
|
|
|
47
53
|
public socket?: WebSocket;
|
|
48
|
-
public
|
|
54
|
+
public wsCustom?: WebSocket | undefined;
|
|
49
55
|
|
|
50
|
-
constructor(ws?: WebSocket) {
|
|
51
|
-
this.
|
|
56
|
+
constructor(options: { ws?: WebSocket; auth?: { username: string; token: string } }) {
|
|
57
|
+
this.wsCustom = options.ws;
|
|
58
|
+
this.auth = options.auth;
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
public setBadges(badges: BadgeURLsBySetIDOrSetIDAndVersion) {
|
|
@@ -67,6 +74,11 @@ export class TwitchIRC {
|
|
|
67
74
|
return this.assets.external_emotes;
|
|
68
75
|
}
|
|
69
76
|
|
|
77
|
+
public authenticate(username: string, token: string) {
|
|
78
|
+
this.bot_name = username;
|
|
79
|
+
this.bot_token = token;
|
|
80
|
+
}
|
|
81
|
+
|
|
70
82
|
public connect(channel?: { channelName?: string }) {
|
|
71
83
|
if (channel?.channelName) this.channel_name = channel.channelName;
|
|
72
84
|
if (!this.channel_name) return console.error('channel_name not specified');
|
|
@@ -76,14 +88,24 @@ export class TwitchIRC {
|
|
|
76
88
|
this.socket?.close();
|
|
77
89
|
|
|
78
90
|
this.socket = new WebSocket('wss://irc-ws.chat.twitch.tv', null, {
|
|
79
|
-
WebSocket: this.
|
|
91
|
+
WebSocket: this.wsCustom,
|
|
80
92
|
});
|
|
81
93
|
this.socket.onopen = () => this.onOpen();
|
|
82
94
|
this.socket.onclose = () => this.onClose();
|
|
83
95
|
this.socket.onmessage = (event) => this.onMessage(event);
|
|
84
96
|
}
|
|
85
97
|
|
|
98
|
+
public send(message: string, replyParentMessageId?: string) {
|
|
99
|
+
if (this.auth === undefined) return console.error('No Auth Information');
|
|
100
|
+
if (!this.isConnected()) return console.error('Not Connected');
|
|
101
|
+
|
|
102
|
+
this.socket.send(
|
|
103
|
+
`${replyParentMessageId ? `@reply-parent-msg-id=${replyParentMessageId} ` : ''}PRIVMSG #${this.channel_name} :${message}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
86
107
|
public disconnect() {
|
|
108
|
+
this.socket?.send(`Part #${this.channel_name}`);
|
|
87
109
|
this.socket?.close();
|
|
88
110
|
}
|
|
89
111
|
|
|
@@ -99,12 +121,18 @@ export class TwitchIRC {
|
|
|
99
121
|
}
|
|
100
122
|
|
|
101
123
|
private onOpen() {
|
|
102
|
-
this.
|
|
103
|
-
this.
|
|
104
|
-
|
|
105
|
-
|
|
124
|
+
this.sendIRC('CAP REQ :twitch.tv/commands twitch.tv/tags');
|
|
125
|
+
if (this.auth !== undefined) {
|
|
126
|
+
this.sendIRC(`PASS oauth:${this.auth.token}`);
|
|
127
|
+
this.sendIRC(`NICK ${this.auth.username}`);
|
|
128
|
+
this.sendIRC(`JOIN #${this.channel_name}`);
|
|
129
|
+
} else {
|
|
130
|
+
this.sendIRC(`PASS ${ANONYMOUS_IRC_PASS}`);
|
|
131
|
+
this.sendIRC(`NICK ${ANONYMOUS_IRC_LOGIN}`);
|
|
132
|
+
this.sendIRC(`JOIN #${this.channel_name}`);
|
|
133
|
+
}
|
|
106
134
|
|
|
107
|
-
|
|
135
|
+
this.public_listeners.connected?.();
|
|
108
136
|
|
|
109
137
|
if (this.ping.interval) clearInterval(this.ping.interval);
|
|
110
138
|
this.sendPing();
|
|
@@ -117,7 +145,7 @@ export class TwitchIRC {
|
|
|
117
145
|
}
|
|
118
146
|
|
|
119
147
|
private sendPing() {
|
|
120
|
-
this.
|
|
148
|
+
this.sendIRC('PING');
|
|
121
149
|
this.ping.lastSentTimestamp = Date.now();
|
|
122
150
|
|
|
123
151
|
if (this.ping.timeout) clearTimeout(this.ping.timeout);
|
|
@@ -127,10 +155,9 @@ export class TwitchIRC {
|
|
|
127
155
|
}, PING_TIMEOUT_MS);
|
|
128
156
|
}
|
|
129
157
|
|
|
130
|
-
private
|
|
131
|
-
if (!this.isConnected())
|
|
132
|
-
|
|
133
|
-
}
|
|
158
|
+
private sendIRC(irc_message: string) {
|
|
159
|
+
if (!this.isConnected()) return console.error('Not Connected');
|
|
160
|
+
|
|
134
161
|
this.socket?.send(irc_message);
|
|
135
162
|
}
|
|
136
163
|
|
|
@@ -149,7 +176,26 @@ export class TwitchIRC {
|
|
|
149
176
|
break;
|
|
150
177
|
}
|
|
151
178
|
case 'PING': {
|
|
152
|
-
this.
|
|
179
|
+
this.sendIRC('PONG');
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case '001': {
|
|
183
|
+
console.log(
|
|
184
|
+
`Connected to Twitch IRC as ${this.auth?.username ?? 'Anonymous'} (${this.channel_name})`,
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
this.public_listeners.connected?.();
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
case 'NOTICE': {
|
|
191
|
+
if (
|
|
192
|
+
message.params[0] === 'Login authentication failed' ||
|
|
193
|
+
message.params[0] === 'Improperly formatted auth'
|
|
194
|
+
) {
|
|
195
|
+
clearTimeout(this.ping.timeout);
|
|
196
|
+
this.disconnect();
|
|
197
|
+
this.public_listeners.auth_error?.();
|
|
198
|
+
}
|
|
153
199
|
break;
|
|
154
200
|
}
|
|
155
201
|
case 'CLEARCHAT': {
|
|
@@ -316,7 +362,7 @@ function parseIRCLine(line: string): IRC_Message {
|
|
|
316
362
|
componentIndex++;
|
|
317
363
|
|
|
318
364
|
let channel: string = '';
|
|
319
|
-
if (components[componentIndex]
|
|
365
|
+
if (components[componentIndex]?.startsWith('#')) channel = components[componentIndex]!.slice(1);
|
|
320
366
|
componentIndex++;
|
|
321
367
|
|
|
322
368
|
const params: string[] = [];
|
|
@@ -430,6 +476,8 @@ const RAW_TAGS = {
|
|
|
430
476
|
|
|
431
477
|
PONG: [],
|
|
432
478
|
|
|
479
|
+
'001': [],
|
|
480
|
+
|
|
433
481
|
PRIVMSG: [
|
|
434
482
|
'badge-info',
|
|
435
483
|
'badges',
|