multichat-ts 0.0.3 → 0.0.5

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/index.d.ts CHANGED
@@ -134,9 +134,10 @@ export type BadgeURLsByNameOrCount = {
134
134
  [badge_count: number]: string;
135
135
  };
136
136
  };
137
- type StringOrAny = '*' | (string & Record<never, never>);
138
- export type BadgeURLsBySetIDAndVersionIDOrAny = {
139
- [badge_set_id: string]: Record<StringOrAny, string>;
137
+ export type BadgeURLsBySetIDOrSetIDAndVersion = {
138
+ [set_id: string]: {
139
+ [version: string]: string;
140
+ } | string;
140
141
  };
141
142
  export type EmoteURLsByName = {
142
143
  [emote_name: string]: string;
package/dist/kick.d.ts CHANGED
@@ -1,19 +1,21 @@
1
1
  import Pusher from 'pusher-js';
2
2
  import { type BadgeURLsByNameOrCount, type EmoteURLsByName, type Message } from './index.js';
3
3
  type EventCallbackFunctions = {
4
- message: (data: Message) => unknown;
4
+ message: (message: Message) => unknown;
5
+ raw_message: (message: ChatMessageEvent) => unknown;
5
6
  };
6
7
  type EventNames = keyof EventCallbackFunctions;
7
8
  export declare class KickPusher {
8
9
  kick_pusher_key: string;
9
10
  channel_name?: string;
10
- assets: {
11
- external_emotes: EmoteURLsByName;
12
- badges: BadgeURLsByNameOrCount;
13
- };
11
+ private assets;
14
12
  private public_listeners;
15
13
  socket?: Pusher;
16
14
  isConnected: boolean;
15
+ setBadges(badges: BadgeURLsByNameOrCount): void;
16
+ setExternalEmotes(external_emotes: EmoteURLsByName): void;
17
+ getStoredBadges(): BadgeURLsByNameOrCount;
18
+ getStoredExternalEmotes(): EmoteURLsByName;
17
19
  connect(channel?: {
18
20
  channelName?: string;
19
21
  }): Promise<void>;
@@ -22,4 +24,24 @@ export declare class KickPusher {
22
24
  private onSubscriptionSuccess;
23
25
  private onChatMessage;
24
26
  }
27
+ interface ChatMessageEvent {
28
+ id: string;
29
+ chatroom_id: number;
30
+ content: string;
31
+ type: string;
32
+ created_at: string;
33
+ sender: {
34
+ id: number;
35
+ username: string;
36
+ slug?: string;
37
+ identity: {
38
+ color: string;
39
+ badges: Array<{
40
+ type: string;
41
+ text: string;
42
+ count?: number;
43
+ }>;
44
+ };
45
+ };
46
+ }
25
47
  export {};
package/dist/kick.js CHANGED
@@ -25,6 +25,18 @@ export class KickPusher {
25
25
  public_listeners = {};
26
26
  socket;
27
27
  isConnected = false;
28
+ setBadges(badges) {
29
+ this.assets.badges = { ...badges, ...this.assets.badges };
30
+ }
31
+ setExternalEmotes(external_emotes) {
32
+ this.assets.external_emotes = { ...external_emotes, ...this.assets.external_emotes };
33
+ }
34
+ getStoredBadges() {
35
+ return this.assets.badges;
36
+ }
37
+ getStoredExternalEmotes() {
38
+ return this.assets.external_emotes;
39
+ }
28
40
  async connect(channel) {
29
41
  if (channel?.channelName)
30
42
  this.channel_name = channel.channelName;
@@ -62,7 +74,7 @@ export class KickPusher {
62
74
  console.log(`Connected to Kick Pusher (${this.channel_name})`);
63
75
  }
64
76
  onChatMessage(data) {
65
- console.log('onMessage', data);
77
+ this.public_listeners.raw_message?.(data);
66
78
  const text = data.content;
67
79
  const emote_matches = [...data.content.matchAll(/\[emote:\d+:[a-zA-Z0-9]*\]/g)];
68
80
  console.log(emote_matches);
package/dist/twitch.d.ts CHANGED
@@ -1,24 +1,26 @@
1
1
  import { WebSocket } from 'partysocket';
2
- import { type ClearMessages, type DeleteMessage, type Message, type Event, type EmoteURLsByName, type BadgeURLsBySetIDAndVersionIDOrAny } from './index.js';
2
+ import { type ClearMessages, type DeleteMessage, type Message, type Event, type EmoteURLsByName, type BadgeURLsBySetIDOrSetIDAndVersion } from './index.js';
3
3
  type EventCallbackFunctions = {
4
- message: (data: Message) => unknown;
4
+ message: (message: Message) => unknown;
5
5
  clear_messages: (data: ClearMessages) => unknown;
6
6
  delete_message: (data: DeleteMessage) => unknown;
7
- event: (data: Event) => unknown;
7
+ event: (event: Event) => unknown;
8
+ raw_message: (message: IRC_Message) => unknown;
8
9
  };
9
10
  type EventNames = keyof EventCallbackFunctions;
10
11
  export declare class TwitchIRC {
11
12
  channel_name?: string;
12
- assets: {
13
- external_emotes: EmoteURLsByName;
14
- badges: BadgeURLsBySetIDAndVersionIDOrAny;
15
- };
13
+ private assets;
16
14
  latency: number;
17
15
  private ping;
18
16
  private public_listeners;
19
17
  socket?: WebSocket;
20
18
  ws?: WebSocket | undefined;
21
19
  constructor(ws?: WebSocket);
20
+ setBadges(badges: BadgeURLsBySetIDOrSetIDAndVersion): void;
21
+ setExternalEmotes(external_emotes: EmoteURLsByName): void;
22
+ getStoredBadges(): BadgeURLsBySetIDOrSetIDAndVersion;
23
+ getStoredExternalEmotes(): EmoteURLsByName;
22
24
  connect(channel?: {
23
25
  channelName?: string;
24
26
  }): void;
@@ -35,4 +37,37 @@ export declare class TwitchIRC {
35
37
  private send;
36
38
  private onMessage;
37
39
  }
40
+ type IRC_Message = {
41
+ [C in CommandType]: {
42
+ channel: string;
43
+ command: C;
44
+ params: string[];
45
+ source: RawSource | undefined;
46
+ tags: SpecificRawTags<C> | undefined;
47
+ };
48
+ }[CommandType];
49
+ type RAW_TAGS = typeof RAW_TAGS;
50
+ type CommandType = keyof RAW_TAGS;
51
+ type SpecificRawTags<Command extends CommandType> = Record<RAW_TAGS[Command][number], string | undefined>;
52
+ type RawSource = {
53
+ host: string;
54
+ nick?: string | undefined;
55
+ user?: string | undefined;
56
+ };
57
+ declare const RAW_TAGS: {
58
+ readonly CLEARCHAT: readonly ["ban-duration", "room-id", "target-user-id", "tmi-sent-ts"];
59
+ readonly CLEARMSG: readonly ["login", "room-id", "target-msg-id", "tmi-sent-ts"];
60
+ readonly GLOBALUSERSTATE: readonly ["badge-info", "badges", "color", "display-name", "emote-sets", "turbo", "user-id", "user-type"];
61
+ readonly HOSTTARGET: readonly [];
62
+ readonly NOTICE: readonly ["msg-id", "target-user-id"];
63
+ readonly PART: readonly [];
64
+ readonly PING: readonly [];
65
+ readonly PONG: readonly [];
66
+ 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
+ readonly RECONNECT: readonly [];
68
+ readonly ROOMSTATE: readonly ["emote-only", "followers-only", "r9k", "room-id", "slow", "subs-only"];
69
+ readonly USERNOTICE: readonly ["badge-info", "badges", "color", "display-name", "emotes", "id", "login", "mod", "msg-id", "room-id", "subscriber", "system-msg", "tmi-sent-ts", "turbo", "user-id", "user-type", "vip", "flags", "msg-param-cumulative-months", "msg-param-displayName", "msg-param-login", "msg-param-multimonth-duration", "msg-param-multimonth-tenure", "msg-param-was-gifted=false", "msg-param-months", "msg-param-promo-gift-total", "msg-param-promo-name", "msg-param-recipient-display-name", "msg-param-recipient-id", "msg-param-recipient-user-name", "msg-param-sender-login", "msg-param-sender-name", "msg-param-should-share-streak", "msg-param-streak-months", "msg-param-sub-plan", "msg-param-sub-plan-name", "msg-param-viewerCount", "msg-param-ritual-name", "msg-param-threshold", "msg-param-gift-months", "msg-param-was-gifted", "msg-param-community-gift-id", "msg-param-mass-gift-count", "msg-param-origin-id"];
70
+ readonly USERSTATE: readonly ["badge-info", "badges", "color", "display-name", "emote-sets", "id", "mod", "subscriber", "turbo", "user-type"];
71
+ readonly WHISPER: readonly ["badges", "color", "display-name", "emotes", "message-id", "thread-id", "turbo", "user-id", "user-type"];
72
+ };
38
73
  export {};
package/dist/twitch.js CHANGED
@@ -17,6 +17,18 @@ export class TwitchIRC {
17
17
  constructor(ws) {
18
18
  this.ws = ws;
19
19
  }
20
+ setBadges(badges) {
21
+ this.assets.badges = { ...badges, ...this.assets.badges };
22
+ }
23
+ setExternalEmotes(external_emotes) {
24
+ this.assets.external_emotes = { ...external_emotes, ...this.assets.external_emotes };
25
+ }
26
+ getStoredBadges() {
27
+ return this.assets.badges;
28
+ }
29
+ getStoredExternalEmotes() {
30
+ return this.assets.external_emotes;
31
+ }
20
32
  connect(channel) {
21
33
  if (channel?.channelName)
22
34
  this.channel_name = channel.channelName;
@@ -77,6 +89,7 @@ export class TwitchIRC {
77
89
  const lines = String(event.data).trim().split('\r\n');
78
90
  const messages = lines.map(parseIRCLine);
79
91
  messages.forEach((message) => {
92
+ this.public_listeners.raw_message?.(message);
80
93
  switch (message.command) {
81
94
  case 'PONG': {
82
95
  if (!this.ping.lastSentTimestamp)
@@ -188,7 +201,10 @@ export class TwitchIRC {
188
201
  const [set_id, version] = badge.split('/');
189
202
  if (!set_id || !version)
190
203
  return [];
191
- const url = this.assets.badges[set_id]?.[version] ?? this.assets.badges[set_id]?.['*'];
204
+ const storedBadge = this.assets.badges[set_id];
205
+ if (!storedBadge)
206
+ return [];
207
+ const url = typeof storedBadge === 'object' ? storedBadge[version] : storedBadge;
192
208
  if (!url)
193
209
  return [];
194
210
  return {
package/package.json CHANGED
@@ -1,52 +1,54 @@
1
1
  {
2
- "name": "multichat-ts",
3
- "version": "0.0.3",
4
- "type": "module",
5
- "description": "Receive type-safe realtime events for chat-related messages on multiple platforms (Twitch, Kick)",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/0Abdullah/multichat-ts.git"
9
- },
10
- "homepage": "https://github.com/0Abdullah/multichat-ts",
11
- "main": "dist/index.js",
12
- "types": "dist/index.d.ts",
13
- "exports": {
14
- ".": "./dist/index.js"
15
- },
16
- "keywords": [
17
- "chat",
18
- "multichat",
19
- "twitch",
20
- "kick",
21
- "realtime",
22
- "irc",
23
- "pusher",
24
- "typescript",
25
- "ts"
26
- ],
27
- "author": {
28
- "name": "Abdullah",
29
- "url": "https://github.com/0Abdullah"
30
- },
31
- "license": "MIT",
32
- "devDependencies": {
33
- "@eslint/js": "^9.10.0",
34
- "@types/node": "^22.5.4",
35
- "eslint": "^9.10.0",
36
- "eslint-config-prettier": "^9.1.0",
37
- "prettier": "^3.3.3",
38
- "typescript": "^5.5.4",
39
- "typescript-eslint": "^8.4.0"
40
- },
41
- "dependencies": {
42
- "partysocket": "^1.0.2",
43
- "pusher-js": "8.4.0-rc2"
44
- },
45
- "scripts": {
46
- "build": "tsc",
47
- "lint": "eslint .",
48
- "lint:fix": "eslint . --fix",
49
- "format": "prettier --check src",
50
- "format:fix": "prettier --write src"
51
- }
52
- }
2
+ "name": "multichat-ts",
3
+ "version": "0.0.5",
4
+ "type": "module",
5
+ "description": "Receive type-safe realtime events for chat-related messages on multiple platforms (Twitch, Kick)",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/0Abdullah/multichat-ts.git"
9
+ },
10
+ "homepage": "https://github.com/0Abdullah/multichat-ts",
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "exports": {
14
+ ".": "./dist/index.js"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "prepublishOnly": "pnpm build",
19
+ "pub": "npm publish --access public",
20
+ "lint": "eslint .",
21
+ "lint:fix": "eslint . --fix",
22
+ "format": "prettier --check src",
23
+ "format:fix": "prettier --write src"
24
+ },
25
+ "keywords": [
26
+ "chat",
27
+ "multichat",
28
+ "twitch",
29
+ "kick",
30
+ "realtime",
31
+ "irc",
32
+ "pusher",
33
+ "typescript",
34
+ "ts"
35
+ ],
36
+ "author": {
37
+ "name": "Abdullah",
38
+ "url": "https://github.com/0Abdullah"
39
+ },
40
+ "license": "MIT",
41
+ "devDependencies": {
42
+ "@eslint/js": "^9.10.0",
43
+ "@types/node": "^22.5.4",
44
+ "eslint": "^9.10.0",
45
+ "eslint-config-prettier": "^9.1.0",
46
+ "prettier": "^3.3.3",
47
+ "typescript": "^5.5.4",
48
+ "typescript-eslint": "^8.4.0"
49
+ },
50
+ "dependencies": {
51
+ "partysocket": "^1.0.2",
52
+ "pusher-js": "8.4.0-rc2"
53
+ }
54
+ }
package/src/index.ts CHANGED
@@ -155,10 +155,12 @@ export type BadgeURLsByNameOrCount = {
155
155
  };
156
156
  };
157
157
 
158
- type StringOrAny = '*' | (string & Record<never, never>);
159
-
160
- export type BadgeURLsBySetIDAndVersionIDOrAny = {
161
- [badge_set_id: string]: Record<StringOrAny, string>;
158
+ export type BadgeURLsBySetIDOrSetIDAndVersion = {
159
+ [set_id: string]:
160
+ | {
161
+ [version: string]: string;
162
+ }
163
+ | string;
162
164
  };
163
165
 
164
166
  export type EmoteURLsByName = {
package/src/kick.ts CHANGED
@@ -8,7 +8,8 @@ import {
8
8
  } from './index.js';
9
9
 
10
10
  type EventCallbackFunctions = {
11
- message: (data: Message) => unknown;
11
+ message: (message: Message) => unknown;
12
+ raw_message: (message: ChatMessageEvent) => unknown;
12
13
  };
13
14
 
14
15
  type EventNames = keyof EventCallbackFunctions;
@@ -18,7 +19,7 @@ const DEFAULT_KICK_PUSHER_KEY = '32cbd69e4b950bf97679';
18
19
  export class KickPusher {
19
20
  public kick_pusher_key = DEFAULT_KICK_PUSHER_KEY;
20
21
  public channel_name?: string;
21
- public assets: {
22
+ private assets: {
22
23
  external_emotes: EmoteURLsByName;
23
24
  badges: BadgeURLsByNameOrCount;
24
25
  } = {
@@ -46,6 +47,22 @@ export class KickPusher {
46
47
  public socket?: Pusher;
47
48
  public isConnected = false;
48
49
 
50
+ public setBadges(badges: BadgeURLsByNameOrCount) {
51
+ this.assets.badges = { ...badges, ...this.assets.badges };
52
+ }
53
+
54
+ public setExternalEmotes(external_emotes: EmoteURLsByName) {
55
+ this.assets.external_emotes = { ...external_emotes, ...this.assets.external_emotes };
56
+ }
57
+
58
+ public getStoredBadges() {
59
+ return this.assets.badges;
60
+ }
61
+
62
+ public getStoredExternalEmotes() {
63
+ return this.assets.external_emotes;
64
+ }
65
+
49
66
  public async connect(channel?: { channelName?: string }) {
50
67
  if (channel?.channelName) this.channel_name = channel.channelName;
51
68
  if (!this.channel_name) return console.error('channel_name not specified');
@@ -109,8 +126,7 @@ export class KickPusher {
109
126
  }
110
127
 
111
128
  private onChatMessage(data: ChatMessageEvent) {
112
- console.log('onMessage', data);
113
-
129
+ this.public_listeners.raw_message?.(data);
114
130
  const text = data.content;
115
131
  const emote_matches = [...data.content.matchAll(/\[emote:\d+:[a-zA-Z0-9]*\]/g)];
116
132
  console.log(emote_matches);
package/src/twitch.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  type Message,
8
8
  type Event,
9
9
  type EmoteURLsByName,
10
- type BadgeURLsBySetIDAndVersionIDOrAny,
10
+ type BadgeURLsBySetIDOrSetIDAndVersion,
11
11
  } from './index.js';
12
12
 
13
13
  const ANONYMOUS_IRC_PASS = 'SCHMOOPIIE';
@@ -16,19 +16,20 @@ const PING_INTERVAL_MS = 30_000;
16
16
  const PING_TIMEOUT_MS = 10_000;
17
17
 
18
18
  type EventCallbackFunctions = {
19
- message: (data: Message) => unknown;
19
+ message: (message: Message) => unknown;
20
20
  clear_messages: (data: ClearMessages) => unknown;
21
21
  delete_message: (data: DeleteMessage) => unknown;
22
- event: (data: Event) => unknown;
22
+ event: (event: Event) => unknown;
23
+ raw_message: (message: IRC_Message) => unknown;
23
24
  };
24
25
 
25
26
  type EventNames = keyof EventCallbackFunctions;
26
27
 
27
28
  export class TwitchIRC {
28
29
  public channel_name?: string;
29
- public assets: {
30
+ private assets: {
30
31
  external_emotes: EmoteURLsByName;
31
- badges: BadgeURLsBySetIDAndVersionIDOrAny;
32
+ badges: BadgeURLsBySetIDOrSetIDAndVersion;
32
33
  } = {
33
34
  external_emotes: {},
34
35
  badges: {},
@@ -50,6 +51,22 @@ export class TwitchIRC {
50
51
  this.ws = ws;
51
52
  }
52
53
 
54
+ public setBadges(badges: BadgeURLsBySetIDOrSetIDAndVersion) {
55
+ this.assets.badges = { ...badges, ...this.assets.badges };
56
+ }
57
+
58
+ public setExternalEmotes(external_emotes: EmoteURLsByName) {
59
+ this.assets.external_emotes = { ...external_emotes, ...this.assets.external_emotes };
60
+ }
61
+
62
+ public getStoredBadges() {
63
+ return this.assets.badges;
64
+ }
65
+
66
+ public getStoredExternalEmotes() {
67
+ return this.assets.external_emotes;
68
+ }
69
+
53
70
  public connect(channel?: { channelName?: string }) {
54
71
  if (channel?.channelName) this.channel_name = channel.channelName;
55
72
  if (!this.channel_name) return console.error('channel_name not specified');
@@ -122,6 +139,7 @@ export class TwitchIRC {
122
139
  const lines = String(event.data).trim().split('\r\n');
123
140
  const messages = lines.map(parseIRCLine);
124
141
  messages.forEach((message) => {
142
+ this.public_listeners.raw_message?.(message);
125
143
  switch (message.command) {
126
144
  case 'PONG': {
127
145
  if (!this.ping.lastSentTimestamp) return console.error('got PONG without sending PING');
@@ -244,8 +262,10 @@ export class TwitchIRC {
244
262
  const [set_id, version] = badge.split('/');
245
263
  if (!set_id || !version) return [];
246
264
 
247
- const url =
248
- this.assets.badges[set_id]?.[version] ?? this.assets.badges[set_id]?.['*'];
265
+ const storedBadge = this.assets.badges[set_id];
266
+ if (!storedBadge) return [];
267
+
268
+ const url = typeof storedBadge === 'object' ? storedBadge[version] : storedBadge;
249
269
  if (!url) return [];
250
270
 
251
271
  return {