multichat-ts 0.0.94 → 0.0.96

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/kick.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import Pusher from 'pusher-js';
2
2
  import { type BadgeURLsByNameOrCount, type EmoteURLsByName, type Message } from './index.js';
3
- type ConnectionState = 'initialized' | 'connecting' | 'connected' | 'unavailable' | 'failed';
3
+ type ConnectionState = 'initialized' | 'connecting' | 'connected' | 'unavailable' | 'failed' | 'disconnected';
4
4
  type ConnectionStateEvent = {
5
5
  previous: ConnectionState;
6
6
  current: ConnectionState;
@@ -18,15 +18,17 @@ export declare class KickPusher {
18
18
  channel_name?: string;
19
19
  private assets;
20
20
  private public_listeners;
21
- socket?: Pusher;
21
+ socket?: Pusher | undefined;
22
22
  isConnected: boolean;
23
23
  setBadges(badges: BadgeURLsByNameOrCount): void;
24
24
  setExternalEmotes(external_emotes: EmoteURLsByName): void;
25
25
  getStoredBadges(): BadgeURLsByNameOrCount;
26
26
  getStoredExternalEmotes(): EmoteURLsByName;
27
+ private connectionVersion;
28
+ private closeSocket;
27
29
  connect(channel?: {
28
30
  channelName?: string;
29
- }, get_channel?: (channelName: string) => Promise<GetChannelResponse | undefined>): Promise<void>;
31
+ }, getChannel?: (channelName: string) => Promise<GetChannelResponse | undefined>): Promise<void>;
30
32
  disconnect(): void;
31
33
  on<EventName extends EventNames>(event_name: EventName, callback_fn: EventCallbackFunctions[EventName]): void;
32
34
  private onSubscriptionSuccess;
package/dist/kick.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import Pusher from 'pusher-js';
2
+ import { buildMessageBody, isFiniteNumber, isRecord, runSafely } from './safety.js';
2
3
  const DEFAULT_KICK_PUSHER_KEY = '32cbd69e4b950bf97679';
3
4
  export class KickPusher {
4
5
  kick_pusher_key = DEFAULT_KICK_PUSHER_KEY;
@@ -37,74 +38,104 @@ export class KickPusher {
37
38
  getStoredExternalEmotes() {
38
39
  return this.assets.external_emotes;
39
40
  }
40
- async connect(channel, get_channel = async (channelName) => {
41
- const res = await fetch(`https://kick.com/api/v2/channels/${channelName}`, {
42
- headers: {
43
- accept: 'aplication/json',
44
- 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
45
- },
46
- });
47
- const json = await res.json();
48
- return json;
49
- }) {
50
- if (channel?.channelName)
41
+ connectionVersion = 0;
42
+ closeSocket() {
43
+ const socket = this.socket;
44
+ this.socket = undefined;
45
+ this.isConnected = false;
46
+ if (!socket)
47
+ return;
48
+ runSafely('kick.cleanup.connection', () => socket.connection.unbind_all());
49
+ runSafely('kick.cleanup.listeners', () => socket.unbind_all());
50
+ runSafely('kick.cleanup.disconnect', () => socket.disconnect());
51
+ }
52
+ async connect(channel, getChannel = defaultGetChannel) {
53
+ if (channel?.channelName) {
51
54
  this.channel_name = channel.channelName;
52
- if (!this.channel_name)
53
- return console.error('channel_name not specified');
54
- console.log(`connecting to ${this.channel_name}...`);
55
- const channel_response = await get_channel(this.channel_name);
56
- if (!channel_response)
57
- return console.error('Failed to connect to Kick.com chat');
58
- channel_response.subscriber_badges.forEach((subscriber_badge) => {
59
- this.assets.badges['subscriber'] = {
60
- ...(this.assets.badges['subscriber'] ?? {}),
61
- [subscriber_badge.months]: subscriber_badge.badge_image.src,
62
- };
63
- });
64
- this.disconnect();
65
- this.socket = new Pusher(this.kick_pusher_key, {
66
- cluster: 'us2',
67
- });
68
- this.socket.subscribe(`chatroom_${channel_response.chatroom.id}`);
69
- this.socket.subscribe(`chatrooms.${channel_response.chatroom.id}.v2`);
70
- this.socket
71
- .bind('pusher:subscription_succeeded', () => this.onSubscriptionSuccess('pusher:subscription_succeeded'))
72
- .bind('App\\Events\\ChatMessageEvent', (data) => this.onChatMessage(data))
73
- .bind('App\\Events\\SubscriptionEvent', (data) => this.onChatSubscription(data))
74
- .bind('GiftedSubscriptionsEvent', (data) => this.onChatGifted(data));
75
- this.socket.connection.bind('state_change', (state) => {
76
- switch (state.current) {
77
- case 'connected': {
78
- this.isConnected = true;
79
- console.log(`Connected to Kick Pusher (${this.channel_name})!`);
80
- break;
81
- }
82
- case 'connecting': {
83
- this.isConnected = false;
84
- console.log(`Connecting to Kick Pusher (${this.channel_name})...`);
85
- break;
86
- }
87
- case 'failed': {
88
- this.isConnected = false;
89
- console.log(`Failed to connect to Kick Pusher (${this.channel_name})`);
90
- break;
91
- }
92
- case 'unavailable': {
93
- this.isConnected = false;
94
- console.log(`Disconnected from Kick Pusher (${this.channel_name})`);
95
- break;
96
- }
97
- default: {
98
- this.isConnected = false;
99
- break;
55
+ }
56
+ const channelName = this.channel_name;
57
+ if (!channelName) {
58
+ throw new Error('Kick channel_name not specified');
59
+ }
60
+ const version = ++this.connectionVersion;
61
+ this.closeSocket();
62
+ const response = await getChannel(channelName);
63
+ if (version !== this.connectionVersion)
64
+ return;
65
+ if (!response || !isFiniteNumber(response.chatroom?.id) || response.chatroom.id <= 0) {
66
+ throw new Error(`Invalid Kick channel response: ${channelName}`);
67
+ }
68
+ const subscriberBadges = {};
69
+ if (Array.isArray(response.subscriber_badges)) {
70
+ for (const badge of response.subscriber_badges) {
71
+ if (badge && isFiniteNumber(badge.months) && typeof badge.badge_image?.src === 'string') {
72
+ subscriberBadges[badge.months] = badge.badge_image.src;
100
73
  }
101
74
  }
102
- this.public_listeners.connection_state_changed?.(state);
103
- });
75
+ }
76
+ this.assets.badges['subscriber'] = subscriberBadges;
77
+ try {
78
+ const socket = new Pusher(this.kick_pusher_key, {
79
+ cluster: 'us2',
80
+ });
81
+ this.socket = socket;
82
+ const bind = (event, handler) => {
83
+ socket.bind(event, (data) => {
84
+ if (this.socket !== socket)
85
+ return;
86
+ runSafely(`kick.${event}`, () => handler(data));
87
+ });
88
+ };
89
+ bind('App\\Events\\ChatMessageEvent', (data) => this.onChatMessage(data));
90
+ bind('App\\Events\\SubscriptionEvent', (data) => this.onChatSubscription(data));
91
+ bind('GiftedSubscriptionsEvent', (data) => this.onChatGifted(data));
92
+ socket.connection.bind('state_change', (state) => {
93
+ if (this.socket !== socket)
94
+ return;
95
+ runSafely('kick.state_change', () => {
96
+ if (!state || typeof state.current !== 'string') {
97
+ throw new Error('Invalid Pusher state-change payload');
98
+ }
99
+ this.isConnected = state.current === 'connected';
100
+ runSafely('kick.connection_state_changed', () => this.public_listeners.connection_state_changed?.(state));
101
+ });
102
+ });
103
+ socket.connection.bind('error', (error) => {
104
+ if (this.socket !== socket)
105
+ return;
106
+ console.error('Kick Pusher connection error', {
107
+ channelName,
108
+ error,
109
+ });
110
+ });
111
+ const names = [`chatroom_${response.chatroom.id}`, `chatrooms.${response.chatroom.id}.v2`];
112
+ for (const name of names) {
113
+ const subscription = socket.subscribe(name);
114
+ subscription.bind('pusher:subscription_succeeded', () => {
115
+ if (this.socket !== socket)
116
+ return;
117
+ runSafely('kick.subscription_succeeded', () => this.onSubscriptionSuccess(name));
118
+ });
119
+ subscription.bind('pusher:subscription_error', (error) => {
120
+ if (this.socket !== socket)
121
+ return;
122
+ console.error('Kick Pusher subscription error', {
123
+ channelName,
124
+ subscription: name,
125
+ error,
126
+ });
127
+ });
128
+ }
129
+ this.isConnected = socket.connection.state === 'connected';
130
+ }
131
+ catch (error) {
132
+ this.closeSocket();
133
+ throw error;
134
+ }
104
135
  }
105
136
  disconnect() {
106
- this.socket?.disconnect();
107
- this.socket?.unbind_all();
137
+ ++this.connectionVersion;
138
+ this.closeSocket();
108
139
  }
109
140
  on(event_name, callback_fn) {
110
141
  this.public_listeners[event_name] = callback_fn;
@@ -113,63 +144,46 @@ export class KickPusher {
113
144
  console.log(`Subscribed to Channel on Kick Pusher (${channel})`);
114
145
  }
115
146
  onChatSubscription(data) {
116
- this.public_listeners.subscription?.(data);
147
+ if (!isSubscription(data)) {
148
+ console.warn('Ignoring invalid Kick subscription payload', JSON.stringify(data));
149
+ return;
150
+ }
151
+ runSafely('kick.subscription', () => {
152
+ this.public_listeners.subscription?.(data);
153
+ });
117
154
  }
118
155
  onChatGifted(data) {
119
- this.public_listeners.gifted?.(data);
156
+ if (!isGifted(data)) {
157
+ console.warn('Ignoring invalid Kick gifted payload', JSON.stringify(data));
158
+ return;
159
+ }
160
+ runSafely('kick.gifted', () => {
161
+ this.public_listeners.gifted?.(data);
162
+ });
120
163
  }
121
164
  onChatMessage(data) {
122
- this.public_listeners.raw_message?.(data);
165
+ if (!isChatMessage(data)) {
166
+ console.warn('Ignoring invalid Kick chat payload: ', JSON.stringify(data));
167
+ return;
168
+ }
169
+ runSafely('kick.raw_message', () => {
170
+ this.public_listeners.raw_message?.(data);
171
+ });
123
172
  const text = data.content;
124
- const emote_matches = [...data.content.matchAll(/\[emote:\d+:.+\]/g)];
125
- const body = [];
126
- emote_matches.forEach((match) => {
127
- const emote_string = match[0];
128
- const emote_parts = emote_string.slice(1, emote_string.length - 1).split(':');
129
- const emote_id = emote_parts[1];
130
- if (!emote_id)
131
- return;
132
- body.push({
173
+ const emotes = [];
174
+ for (const match of text.matchAll(/\[emote:(\d+):[^\]]+\]/g)) {
175
+ const id = match[1];
176
+ const start = match.index;
177
+ if (!id || start === undefined)
178
+ continue;
179
+ emotes.push({
133
180
  type: 'emote',
134
- start_inclusive: match.index,
135
- end_exclusive: match.index + emote_string.length,
136
- url: `https://files.kick.com/emotes/${emote_id}/fullsize`,
137
- });
138
- });
139
- body.sort((a, b) => a.start_inclusive - b.start_inclusive);
140
- const old_body_length = body.length;
141
- if (old_body_length > 0) {
142
- body.forEach((segment, index) => {
143
- const previous_segment = body[index - 1];
144
- const text_start_inclusive = previous_segment?.end_exclusive !== undefined ? previous_segment.end_exclusive + 1 : 0;
145
- const text_end_exclusive = Math.max(0, segment.start_inclusive);
146
- if (text_end_exclusive - text_start_inclusive > 0) {
147
- body.push({
148
- type: 'text',
149
- text: text.slice(text_start_inclusive, text_end_exclusive),
150
- start_inclusive: text_start_inclusive,
151
- end_exclusive: text_end_exclusive,
152
- });
153
- }
154
- if (index === old_body_length - 1 && segment.end_exclusive < text.length - 1) {
155
- body.push({
156
- type: 'text',
157
- text: text.slice(segment.end_exclusive),
158
- start_inclusive: segment.end_exclusive,
159
- end_exclusive: text.length,
160
- });
161
- }
162
- });
163
- }
164
- else {
165
- body.push({
166
- type: 'text',
167
- text,
168
- start_inclusive: 0,
169
- end_exclusive: text.length,
181
+ start_inclusive: start,
182
+ end_exclusive: start + match[0].length,
183
+ url: `https://files.kick.com/emotes/${id}/fullsize`,
170
184
  });
171
185
  }
172
- body.sort((a, b) => a.start_inclusive - b.start_inclusive);
186
+ const body = buildMessageBody(text, emotes);
173
187
  const message = {
174
188
  id: data.id,
175
189
  user: {
@@ -184,12 +198,14 @@ export class KickPusher {
184
198
  const badge_count = badge.count;
185
199
  if (typeof badge_url_or_counts === 'string')
186
200
  badge_url = badge_url_or_counts;
187
- else if (typeof badge_url_or_counts === 'object' && badge_count !== undefined) {
188
- const badge_entry_by_count = Object.entries(badge_url_or_counts)
189
- .sort(([a_min_count], [b_min_count]) => Number(a_min_count) - Number(b_min_count))
190
- .find(([min_count]) => badge_count >= Number(min_count));
191
- if (badge_entry_by_count)
192
- badge_url = badge_entry_by_count[1];
201
+ else if (badge_url_or_counts !== null &&
202
+ typeof badge_url_or_counts === 'object' &&
203
+ isFiniteNumber(badge_count)) {
204
+ const entry = Object.entries(badge_url_or_counts)
205
+ .filter(([minimum, url]) => Number.isFinite(Number(minimum)) && typeof url === 'string')
206
+ .sort(([a], [b]) => Number(b) - Number(a))
207
+ .find(([minimum]) => badge_count >= Number(minimum));
208
+ badge_url = entry?.[1];
193
209
  }
194
210
  if (!badge_url)
195
211
  return [];
@@ -208,13 +224,76 @@ export class KickPusher {
208
224
  raw_text: data.content,
209
225
  timestamp_sent: Date.parse(data.created_at),
210
226
  };
211
- if (data.type === 'celebration' && data.metadata?.celebration) {
227
+ const metadata = data.metadata;
228
+ const celebration = isRecord(metadata) ? metadata['celebration'] : undefined;
229
+ if (data.type === 'celebration' &&
230
+ isRecord(celebration) &&
231
+ typeof celebration['id'] === 'string' &&
232
+ isFiniteNumber(celebration['total_months']) &&
233
+ celebration['total_months'] > 0 &&
234
+ typeof celebration['created_at'] === 'string' &&
235
+ Number.isFinite(Date.parse(celebration['created_at']))) {
212
236
  message.resubscription = {
213
- id: data.metadata.celebration.id,
214
- months: data.metadata.celebration.total_months,
215
- subscribed_since_timestamp: data.metadata.celebration.created_at,
237
+ id: celebration['id'],
238
+ months: celebration['total_months'],
239
+ subscribed_since_timestamp: celebration['created_at'],
216
240
  };
217
241
  }
218
- this.public_listeners.message?.(message);
242
+ runSafely('kick.message', () => {
243
+ this.public_listeners.message?.(message);
244
+ });
219
245
  }
220
246
  }
247
+ async function defaultGetChannel(channelName) {
248
+ const url = 'https://kick.com/api/v2/channels/' + encodeURIComponent(channelName);
249
+ const response = await fetch(url, {
250
+ headers: {
251
+ accept: 'application/json',
252
+ },
253
+ signal: AbortSignal.timeout(15_000),
254
+ });
255
+ if (!response.ok) {
256
+ throw new Error(`Kick channel lookup HTTP ${response.status}`);
257
+ }
258
+ return (await response.json());
259
+ }
260
+ function isChatMessage(value) {
261
+ if (!isRecord(value))
262
+ return false;
263
+ const sender = value['sender'];
264
+ if (typeof value['id'] !== 'string' ||
265
+ !isFiniteNumber(value['chatroom_id']) ||
266
+ typeof value['content'] !== 'string' ||
267
+ typeof value['created_at'] !== 'string' ||
268
+ !Number.isFinite(Date.parse(value['created_at'])) ||
269
+ !['message', 'celebration', 'reply'].includes(String(value['type'])) ||
270
+ !isRecord(sender) ||
271
+ !isFiniteNumber(sender['id']) ||
272
+ typeof sender['username'] !== 'string' ||
273
+ (sender['slug'] !== undefined && typeof sender['slug'] !== 'string')) {
274
+ return false;
275
+ }
276
+ const identity = sender['identity'];
277
+ return (isRecord(identity) &&
278
+ typeof identity['color'] === 'string' &&
279
+ Array.isArray(identity['badges']) &&
280
+ identity['badges'].every((badge) => isRecord(badge) &&
281
+ typeof badge['type'] === 'string' &&
282
+ typeof badge['text'] === 'string' &&
283
+ (badge['count'] === undefined || isFiniteNumber(badge['count']))));
284
+ }
285
+ function isSubscription(value) {
286
+ return (isRecord(value) &&
287
+ isFiniteNumber(value['chatroom_id']) &&
288
+ typeof value['username'] === 'string' &&
289
+ isFiniteNumber(value['months']) &&
290
+ value['months'] > 0);
291
+ }
292
+ function isGifted(value) {
293
+ return (isRecord(value) &&
294
+ isFiniteNumber(value['chatroom_id']) &&
295
+ typeof value['gifter_username'] === 'string' &&
296
+ isFiniteNumber(value['gifter_total']) &&
297
+ Array.isArray(value['gifted_usernames']) &&
298
+ value['gifted_usernames'].every((name) => typeof name === 'string'));
299
+ }
@@ -0,0 +1,5 @@
1
+ import { BodyComponent } from '.';
2
+ export declare function runSafely(context: string, callback: () => unknown): void;
3
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
4
+ export declare function isFiniteNumber(value: unknown): value is number;
5
+ export declare function buildMessageBody(text: string, emotes: BodyComponent[]): BodyComponent[];
package/dist/safety.js ADDED
@@ -0,0 +1,56 @@
1
+ export function runSafely(context, callback) {
2
+ try {
3
+ const result = callback();
4
+ if (result !== null &&
5
+ result !== undefined &&
6
+ typeof result.then === 'function') {
7
+ void Promise.resolve(result).catch((error) => {
8
+ console.error(`[${context}] Async failure`, error);
9
+ });
10
+ }
11
+ }
12
+ catch (error) {
13
+ console.error(`[${context}] Failure`, error);
14
+ }
15
+ }
16
+ export function isRecord(value) {
17
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
18
+ }
19
+ export function isFiniteNumber(value) {
20
+ return typeof value === 'number' && Number.isFinite(value);
21
+ }
22
+ export function buildMessageBody(text, emotes) {
23
+ const sorted = [...emotes].sort((a, b) => a.start_inclusive - b.start_inclusive);
24
+ const body = [];
25
+ let cursor = 0;
26
+ for (const emote of sorted) {
27
+ const start = emote.start_inclusive;
28
+ const end = emote.end_exclusive;
29
+ if (!Number.isInteger(start) ||
30
+ !Number.isInteger(end) ||
31
+ start < cursor ||
32
+ end <= start ||
33
+ end > text.length) {
34
+ continue;
35
+ }
36
+ if (start > cursor) {
37
+ body.push({
38
+ type: 'text',
39
+ text: text.slice(cursor, start),
40
+ start_inclusive: cursor,
41
+ end_exclusive: start,
42
+ });
43
+ }
44
+ body.push(emote);
45
+ cursor = end;
46
+ }
47
+ if (cursor < text.length || body.length === 0) {
48
+ body.push({
49
+ type: 'text',
50
+ text: text.slice(cursor),
51
+ start_inclusive: cursor,
52
+ end_exclusive: text.length,
53
+ });
54
+ }
55
+ return body;
56
+ }
package/dist/test.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/test.js ADDED
@@ -0,0 +1,24 @@
1
+ import { format } from 'util';
2
+ import { KickPusher } from './kick.js';
3
+ const originalError = console.error.bind(console);
4
+ const originalWarn = console.warn.bind(console);
5
+ console.warn = (...args) => {
6
+ originalWarn(`\x1b[33m${format(...args)}\x1b[0m`);
7
+ };
8
+ console.error = (...args) => {
9
+ originalError(`\x1b[31m${format(...args)}\x1b[0m`);
10
+ };
11
+ const client = new KickPusher();
12
+ client.on('subscription', (data) => {
13
+ console.log(JSON.stringify(data));
14
+ });
15
+ client.on('connection_state_changed', (data) => {
16
+ console.log(JSON.stringify(data));
17
+ });
18
+ client.on('gifted', (data) => {
19
+ console.log(JSON.stringify(data));
20
+ });
21
+ client.on('message', (data) => {
22
+ console.log(JSON.stringify(data));
23
+ });
24
+ client.connect({ channelName: 'drb7h' });
package/dist/twitch.d.ts CHANGED
@@ -6,25 +6,44 @@ 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;
13
+ type SocketOptions = NonNullable<ConstructorParameters<typeof WebSocket>[2]>;
11
14
  export declare class TwitchIRC {
12
15
  channel_name?: string;
16
+ auth?: {
17
+ username: string;
18
+ token: string;
19
+ } | undefined;
20
+ bot_name?: string;
21
+ bot_token?: string;
13
22
  private assets;
14
23
  latency: number;
15
24
  private ping;
16
25
  private public_listeners;
17
- socket?: WebSocket;
18
- ws?: WebSocket | undefined;
19
- constructor(ws?: WebSocket);
26
+ socket?: WebSocket | undefined;
27
+ wsCustom?: SocketOptions['WebSocket'];
28
+ constructor(options?: {
29
+ ws?: SocketOptions['WebSocket'];
30
+ auth?: {
31
+ username: string;
32
+ token: string;
33
+ };
34
+ });
20
35
  setBadges(badges: BadgeURLsBySetIDOrSetIDAndVersion): void;
21
- setExternalEmotes(external_emotes: EmoteURLsByName): void;
36
+ setExternalEmotes(externalEmotes: EmoteURLsByName): void;
22
37
  getStoredBadges(): BadgeURLsBySetIDOrSetIDAndVersion;
23
38
  getStoredExternalEmotes(): EmoteURLsByName;
39
+ authenticate(username: string, token: string): void;
40
+ private clearPing;
24
41
  connect(channel?: {
25
42
  channelName?: string;
26
43
  }): void;
44
+ send(message: string, replyParentMessageId?: string): void;
27
45
  disconnect(): void;
46
+ private restartConnection;
28
47
  on<EventName extends EventNames>(event_name: EventName, callback_fn: EventCallbackFunctions[EventName]): void;
29
48
  isConnected(): this is {
30
49
  socket: {
@@ -32,42 +51,22 @@ export declare class TwitchIRC {
32
51
  } & WebSocket;
33
52
  };
34
53
  private onOpen;
35
- private onClose;
36
54
  private sendPing;
37
- private send;
55
+ private sendIRC;
38
56
  private onMessage;
57
+ private handleIRCMessage;
39
58
  }
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>;
59
+ export declare function parseIRCLine(line: string): IRC_Message | undefined;
60
+ export type IRC_Message = {
61
+ channel: string;
62
+ command: string;
63
+ params: string[];
64
+ source: RawSource | undefined;
65
+ tags: Record<string, string | undefined> | undefined;
66
+ };
52
67
  type RawSource = {
53
68
  host: string;
54
69
  nick?: string | undefined;
55
70
  user?: string | undefined;
56
71
  };
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
- };
73
72
  export {};