multichat-ts 0.0.1 → 0.0.3
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 +80 -0
- package/dist/index.d.ts +5 -6
- package/dist/index.js +2 -2
- package/dist/kick.d.ts +1 -1
- package/dist/kick.js +0 -1
- package/dist/twitch.d.ts +4 -2
- package/dist/twitch.js +9 -5
- package/package.json +2 -2
- package/src/index.ts +6 -6
- package/src/kick.ts +1 -1
- package/src/twitch.ts +14 -13
- package/tsconfig.json +36 -36
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# multichat-ts
|
|
2
|
+
|
|
3
|
+
Receive type-safe realtime events for chat-related messages on multiple platforms (Twitch, Kick)
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm install multichat-ts # or (npm, bun, deno, etc) install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { TwitchIRC, KickPusher } from 'multichat-ts';
|
|
13
|
+
|
|
14
|
+
const twitch_chat = new TwitchIRC();
|
|
15
|
+
const kick_chat = new KickPusher();
|
|
16
|
+
|
|
17
|
+
twitch_chat.connect({
|
|
18
|
+
channelName: 'piratesoftware',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
kick_chat.connect({
|
|
22
|
+
channelName: 'xqc',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// set custom badge assets or external emotes (ffz, bttv, 7tv, etc)
|
|
26
|
+
twitch_chat.assets.external_emotes = {
|
|
27
|
+
OMEGALUL: 'https://cdn.frankerfacez.com/emoticon/128054/1',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
twitch_chat.on('message', (message) => {
|
|
31
|
+
console.log('new twitch message', message.raw_text, message);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
kick_chat.on('message', (message) => {
|
|
35
|
+
console.log('new twitch message', message.raw_text, message);
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Example usage with Svelte 5
|
|
40
|
+
|
|
41
|
+
```svelte
|
|
42
|
+
<script lang="ts">
|
|
43
|
+
import { TwitchIRC, type Message } from 'multichat-ts';
|
|
44
|
+
|
|
45
|
+
let messages: Message[] = $state([]);
|
|
46
|
+
|
|
47
|
+
const twitch_chat = new TwitchIRC();
|
|
48
|
+
$effect(() => {
|
|
49
|
+
twitch_chat.connect({
|
|
50
|
+
channelName: 'piratesoftware'
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return () => twitch_chat.disconnect();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
twitch_chat.on('message', (message) => {
|
|
57
|
+
messages.push(message);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
twitch_chat.on('clear_messages', (data) => {
|
|
61
|
+
if (data.user !== undefined) {
|
|
62
|
+
messages.filter((message) => message.user.id !== data.user!.id);
|
|
63
|
+
} else {
|
|
64
|
+
messages.filter((message) => message.channel.room_id !== data.channel.room_id);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
twitch_chat.on('delete_message', (message_data) => {
|
|
69
|
+
messages.filter((message) => message.id !== message_data.id);
|
|
70
|
+
});
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<ul>
|
|
74
|
+
{#each messages as message}
|
|
75
|
+
<li>{message.user.display_name}: {message.raw_text}</li>
|
|
76
|
+
{/each}
|
|
77
|
+
</ul>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from './kick';
|
|
2
|
-
export * from './twitch';
|
|
1
|
+
export * from './kick.js';
|
|
2
|
+
export * from './twitch.js';
|
|
3
3
|
export type Message = {
|
|
4
4
|
body: BodyComponent[];
|
|
5
5
|
channel: {
|
|
@@ -134,10 +134,9 @@ export type BadgeURLsByNameOrCount = {
|
|
|
134
134
|
[badge_count: number]: string;
|
|
135
135
|
};
|
|
136
136
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
};
|
|
137
|
+
type StringOrAny = '*' | (string & Record<never, never>);
|
|
138
|
+
export type BadgeURLsBySetIDAndVersionIDOrAny = {
|
|
139
|
+
[badge_set_id: string]: Record<StringOrAny, string>;
|
|
141
140
|
};
|
|
142
141
|
export type EmoteURLsByName = {
|
|
143
142
|
[emote_name: string]: string;
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './kick';
|
|
2
|
-
export * from './twitch';
|
|
1
|
+
export * from './kick.js';
|
|
2
|
+
export * from './twitch.js';
|
package/dist/kick.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Pusher from 'pusher-js';
|
|
2
|
-
import { type BadgeURLsByNameOrCount, type EmoteURLsByName, type Message } from './index';
|
|
2
|
+
import { type BadgeURLsByNameOrCount, type EmoteURLsByName, type Message } from './index.js';
|
|
3
3
|
type EventCallbackFunctions = {
|
|
4
4
|
message: (data: Message) => unknown;
|
|
5
5
|
};
|
package/dist/kick.js
CHANGED
package/dist/twitch.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WebSocket } from 'partysocket';
|
|
2
|
-
import { type ClearMessages, type DeleteMessage, type Message, type Event, type EmoteURLsByName, type
|
|
2
|
+
import { type ClearMessages, type DeleteMessage, type Message, type Event, type EmoteURLsByName, type BadgeURLsBySetIDAndVersionIDOrAny } from './index.js';
|
|
3
3
|
type EventCallbackFunctions = {
|
|
4
4
|
message: (data: Message) => unknown;
|
|
5
5
|
clear_messages: (data: ClearMessages) => unknown;
|
|
@@ -11,12 +11,14 @@ export declare class TwitchIRC {
|
|
|
11
11
|
channel_name?: string;
|
|
12
12
|
assets: {
|
|
13
13
|
external_emotes: EmoteURLsByName;
|
|
14
|
-
badges:
|
|
14
|
+
badges: BadgeURLsBySetIDAndVersionIDOrAny;
|
|
15
15
|
};
|
|
16
16
|
latency: number;
|
|
17
17
|
private ping;
|
|
18
18
|
private public_listeners;
|
|
19
19
|
socket?: WebSocket;
|
|
20
|
+
ws?: WebSocket | undefined;
|
|
21
|
+
constructor(ws?: WebSocket);
|
|
20
22
|
connect(channel?: {
|
|
21
23
|
channelName?: string;
|
|
22
24
|
}): void;
|
package/dist/twitch.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { WebSocket } from 'partysocket';
|
|
2
|
-
import {} from './index';
|
|
3
2
|
const ANONYMOUS_IRC_PASS = 'SCHMOOPIIE';
|
|
4
3
|
const ANONYMOUS_IRC_LOGIN = 'justinfan1234';
|
|
5
4
|
const PING_INTERVAL_MS = 30_000;
|
|
@@ -14,6 +13,10 @@ export class TwitchIRC {
|
|
|
14
13
|
ping = {};
|
|
15
14
|
public_listeners = {};
|
|
16
15
|
socket;
|
|
16
|
+
ws;
|
|
17
|
+
constructor(ws) {
|
|
18
|
+
this.ws = ws;
|
|
19
|
+
}
|
|
17
20
|
connect(channel) {
|
|
18
21
|
if (channel?.channelName)
|
|
19
22
|
this.channel_name = channel.channelName;
|
|
@@ -21,11 +24,12 @@ export class TwitchIRC {
|
|
|
21
24
|
return console.error('channel_name not specified');
|
|
22
25
|
console.log(`connecting to ${this.channel_name}...`);
|
|
23
26
|
this.socket?.close();
|
|
24
|
-
this.socket = new WebSocket('wss://irc-ws.chat.twitch.tv'
|
|
27
|
+
this.socket = new WebSocket('wss://irc-ws.chat.twitch.tv', null, {
|
|
28
|
+
WebSocket: this.ws,
|
|
29
|
+
});
|
|
25
30
|
this.socket.onopen = () => this.onOpen();
|
|
26
31
|
this.socket.onclose = () => this.onClose();
|
|
27
32
|
this.socket.onmessage = (event) => this.onMessage(event);
|
|
28
|
-
console.log(parseIRCLine(`@badge-info=;badges=turbo/1;color=#9ACD32;display-name=TestChannel;emotes=;id=3d830f12-795c-447d-af3c-ea05e40fbddb;login=testchannel;mod=0;msg-id=raid;msg-param-displayName=TestChannel;msg-param-login=testchannel;msg-param-viewerCount=15;room-id=33332222;subscriber=0;system-msg=15\sraiders\sfrom\sTestChannel\shave\sjoined\n!;tmi-sent-ts=1507246572675;turbo=1;user-id=123456;user-type= :tmi.twitch.tv USERNOTICE #othertestchannel`));
|
|
29
33
|
}
|
|
30
34
|
disconnect() {
|
|
31
35
|
this.socket?.close();
|
|
@@ -184,7 +188,7 @@ export class TwitchIRC {
|
|
|
184
188
|
const [set_id, version] = badge.split('/');
|
|
185
189
|
if (!set_id || !version)
|
|
186
190
|
return [];
|
|
187
|
-
const url = this.assets.badges[set_id]?.[version];
|
|
191
|
+
const url = this.assets.badges[set_id]?.[version] ?? this.assets.badges[set_id]?.['*'];
|
|
188
192
|
if (!url)
|
|
189
193
|
return [];
|
|
190
194
|
return {
|
|
@@ -195,7 +199,7 @@ export class TwitchIRC {
|
|
|
195
199
|
}) ?? [],
|
|
196
200
|
color: tags.color ?? '#FFFFFF',
|
|
197
201
|
id: tags['user-id'],
|
|
198
|
-
username: '
|
|
202
|
+
username: (tags['display-name'] ?? 'Unknown').toLowerCase(),
|
|
199
203
|
display_name: tags['display-name'] ?? 'Unknown',
|
|
200
204
|
roles: {},
|
|
201
205
|
},
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "multichat-ts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Receive
|
|
5
|
+
"description": "Receive type-safe realtime events for chat-related messages on multiple platforms (Twitch, Kick)",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/0Abdullah/multichat-ts.git"
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from './kick';
|
|
2
|
-
export * from './twitch';
|
|
1
|
+
export * from './kick.js';
|
|
2
|
+
export * from './twitch.js';
|
|
3
3
|
|
|
4
4
|
export type Message = {
|
|
5
5
|
body: BodyComponent[];
|
|
@@ -155,10 +155,10 @@ export type BadgeURLsByNameOrCount = {
|
|
|
155
155
|
};
|
|
156
156
|
};
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
type StringOrAny = '*' | (string & Record<never, never>);
|
|
159
|
+
|
|
160
|
+
export type BadgeURLsBySetIDAndVersionIDOrAny = {
|
|
161
|
+
[badge_set_id: string]: Record<StringOrAny, string>;
|
|
162
162
|
};
|
|
163
163
|
|
|
164
164
|
export type EmoteURLsByName = {
|
package/src/kick.ts
CHANGED
package/src/twitch.ts
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
type Message,
|
|
8
8
|
type Event,
|
|
9
9
|
type EmoteURLsByName,
|
|
10
|
-
type
|
|
11
|
-
} from './index';
|
|
10
|
+
type BadgeURLsBySetIDAndVersionIDOrAny,
|
|
11
|
+
} from './index.js';
|
|
12
12
|
|
|
13
13
|
const ANONYMOUS_IRC_PASS = 'SCHMOOPIIE';
|
|
14
14
|
const ANONYMOUS_IRC_LOGIN = 'justinfan1234';
|
|
@@ -28,7 +28,7 @@ export class TwitchIRC {
|
|
|
28
28
|
public channel_name?: string;
|
|
29
29
|
public assets: {
|
|
30
30
|
external_emotes: EmoteURLsByName;
|
|
31
|
-
badges:
|
|
31
|
+
badges: BadgeURLsBySetIDAndVersionIDOrAny;
|
|
32
32
|
} = {
|
|
33
33
|
external_emotes: {},
|
|
34
34
|
badges: {},
|
|
@@ -44,6 +44,11 @@ export class TwitchIRC {
|
|
|
44
44
|
private public_listeners: Partial<EventCallbackFunctions> = {};
|
|
45
45
|
|
|
46
46
|
public socket?: WebSocket;
|
|
47
|
+
public ws?: WebSocket | undefined;
|
|
48
|
+
|
|
49
|
+
constructor(ws?: WebSocket) {
|
|
50
|
+
this.ws = ws;
|
|
51
|
+
}
|
|
47
52
|
|
|
48
53
|
public connect(channel?: { channelName?: string }) {
|
|
49
54
|
if (channel?.channelName) this.channel_name = channel.channelName;
|
|
@@ -53,17 +58,12 @@ export class TwitchIRC {
|
|
|
53
58
|
|
|
54
59
|
this.socket?.close();
|
|
55
60
|
|
|
56
|
-
this.socket = new WebSocket('wss://irc-ws.chat.twitch.tv'
|
|
61
|
+
this.socket = new WebSocket('wss://irc-ws.chat.twitch.tv', null, {
|
|
62
|
+
WebSocket: this.ws,
|
|
63
|
+
});
|
|
57
64
|
this.socket.onopen = () => this.onOpen();
|
|
58
65
|
this.socket.onclose = () => this.onClose();
|
|
59
66
|
this.socket.onmessage = (event) => this.onMessage(event);
|
|
60
|
-
|
|
61
|
-
console.log(
|
|
62
|
-
parseIRCLine(
|
|
63
|
-
// eslint-disable-next-line no-useless-escape
|
|
64
|
-
`@badge-info=;badges=turbo/1;color=#9ACD32;display-name=TestChannel;emotes=;id=3d830f12-795c-447d-af3c-ea05e40fbddb;login=testchannel;mod=0;msg-id=raid;msg-param-displayName=TestChannel;msg-param-login=testchannel;msg-param-viewerCount=15;room-id=33332222;subscriber=0;system-msg=15\sraiders\sfrom\sTestChannel\shave\sjoined\n!;tmi-sent-ts=1507246572675;turbo=1;user-id=123456;user-type= :tmi.twitch.tv USERNOTICE #othertestchannel`,
|
|
65
|
-
),
|
|
66
|
-
);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
public disconnect() {
|
|
@@ -244,7 +244,8 @@ export class TwitchIRC {
|
|
|
244
244
|
const [set_id, version] = badge.split('/');
|
|
245
245
|
if (!set_id || !version) return [];
|
|
246
246
|
|
|
247
|
-
const url =
|
|
247
|
+
const url =
|
|
248
|
+
this.assets.badges[set_id]?.[version] ?? this.assets.badges[set_id]?.['*'];
|
|
248
249
|
if (!url) return [];
|
|
249
250
|
|
|
250
251
|
return {
|
|
@@ -255,7 +256,7 @@ export class TwitchIRC {
|
|
|
255
256
|
}) ?? [],
|
|
256
257
|
color: tags.color ?? '#FFFFFF',
|
|
257
258
|
id: tags['user-id'],
|
|
258
|
-
username: '
|
|
259
|
+
username: (tags['display-name'] ?? 'Unknown').toLowerCase(),
|
|
259
260
|
display_name: tags['display-name'] ?? 'Unknown',
|
|
260
261
|
roles: {},
|
|
261
262
|
},
|
package/tsconfig.json
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
/* Visit https://aka.ms/tsconfig to read more about this file */
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/* Language and Environment */
|
|
6
|
+
"target": "ESNext",
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/* Modules */
|
|
9
|
+
"module": "ESNext",
|
|
10
|
+
"moduleResolution": "Bundler",
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
/* JavaScript Support */
|
|
13
|
+
"checkJs": true,
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
/* Emit */
|
|
16
|
+
"declaration": true,
|
|
17
|
+
"outDir": "dist",
|
|
18
|
+
"removeComments": true,
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
/* Interop Constraints */
|
|
21
|
+
"isolatedModules": true,
|
|
22
|
+
"verbatimModuleSyntax": false,
|
|
23
|
+
"allowSyntheticDefaultImports": true,
|
|
24
|
+
"esModuleInterop": true,
|
|
25
|
+
"forceConsistentCasingInFileNames": true,
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
/* Type Checking */
|
|
28
|
+
"strict": true,
|
|
29
|
+
"strictNullChecks": true,
|
|
30
|
+
"noUnusedLocals": true,
|
|
31
|
+
"noUnusedParameters": true,
|
|
32
|
+
"exactOptionalPropertyTypes": true,
|
|
33
|
+
"noImplicitReturns": true,
|
|
34
|
+
"noFallthroughCasesInSwitch": true,
|
|
35
|
+
"noUncheckedIndexedAccess": true,
|
|
36
|
+
"noImplicitOverride": true,
|
|
37
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
38
|
+
"allowUnusedLabels": false,
|
|
39
|
+
"allowUnreachableCode": false,
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
/* Completeness */
|
|
42
|
+
"skipLibCheck": true
|
|
43
|
+
},
|
|
44
|
+
"include": ["src"]
|
|
45
45
|
}
|