@manaobot/kick 1.0.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.
Files changed (47) hide show
  1. package/README.md +163 -0
  2. package/biome.json +44 -0
  3. package/bun.lock +78 -0
  4. package/example/01-authorize-bot/.env.example +7 -0
  5. package/example/01-authorize-bot/index.ts +60 -0
  6. package/example/02-webhook/.env.example +7 -0
  7. package/example/02-webhook/index.ts +80 -0
  8. package/example/03-ngrok/.env.example +7 -0
  9. package/example/03-ngrok/index.ts +92 -0
  10. package/example/04-categories-api/.env.example +7 -0
  11. package/example/04-categories-api/index.ts +77 -0
  12. package/example/05-users-api/.env.example +7 -0
  13. package/example/05-users-api/index.ts +60 -0
  14. package/example/06-channels-api/.env.example +7 -0
  15. package/example/06-channels-api/index.ts +60 -0
  16. package/example/07-channel-rewards-api/.env.example +7 -0
  17. package/example/07-channel-rewards-api/index.ts +60 -0
  18. package/example/08-basic-chat-bot/.env.example +7 -0
  19. package/example/08-basic-chat-bot/index.ts +102 -0
  20. package/package.json +23 -0
  21. package/qodana.yaml +31 -0
  22. package/src/KickClient.ts +172 -0
  23. package/src/Logger.ts +25 -0
  24. package/src/api/CategoriesAPI.ts +45 -0
  25. package/src/api/ChannelRewardsAPI.ts +121 -0
  26. package/src/api/ChannelsAPI.ts +63 -0
  27. package/src/api/KicksAPI.ts +37 -0
  28. package/src/api/LivestreamsAPI.ts +65 -0
  29. package/src/api/ModerationAPI.ts +60 -0
  30. package/src/api/UsersAPI.ts +72 -0
  31. package/src/auth/AuthManager.ts +64 -0
  32. package/src/auth/CallbackServer.ts +57 -0
  33. package/src/auth/OAuth.ts +55 -0
  34. package/src/auth/PKCE.ts +13 -0
  35. package/src/auth/TokenManager.ts +53 -0
  36. package/src/chat/ChatClient.ts +48 -0
  37. package/src/rest/RestClient.ts +39 -0
  38. package/src/webhooks/NgrokAdapter.ts +46 -0
  39. package/src/webhooks/WebhookRouter.ts +135 -0
  40. package/src/webhooks/WebhookServer.ts +41 -0
  41. package/tsconfig.json +29 -0
  42. package/types/api.d.ts +158 -0
  43. package/types/auth.d.ts +38 -0
  44. package/types/chat.d.ts +14 -0
  45. package/types/client.d.ts +67 -0
  46. package/types/index.d.ts +4 -0
  47. package/types/webhooks.d.ts +35 -0
@@ -0,0 +1,48 @@
1
+ import type { RestClient } from "../rest/RestClient.ts";
2
+ import type { ChatMessage } from "../../types";
3
+
4
+ /**
5
+ * ChatClient provides access to Kick Chat APIs.
6
+ * @example
7
+ * await kick.chat.send({
8
+ * content: "Hello world!"
9
+ * });
10
+ */
11
+ export class ChatClient {
12
+ constructor(private rest: RestClient) {}
13
+
14
+ /**
15
+ * Send a message to the chat.
16
+ * * Requires scope:
17
+ * `chat:write`
18
+ * @param message The message object to send
19
+ * @returns A promise that resolves when the message is sent
20
+ * @example
21
+ * // Send a simple message
22
+ * await kick.chat.send({ content: "Hello Kick!" });
23
+ *
24
+ */
25
+ send(message: ChatMessage): Promise<void> {
26
+ return this.rest.fetch("/public/v1/chat", {
27
+ method: "POST",
28
+ body: JSON.stringify({
29
+ type: message.type ?? "bot",
30
+ content: message.content,
31
+ }),
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Delete a chat message from a channel.
37
+ *
38
+ * * Requires scope:
39
+ * `moderation:chat_message:manage`: Execute moderation actions on chat messages
40
+ *
41
+ * @param messageId Message ID
42
+ */
43
+ delete(messageId: string): Promise<void> {
44
+ return this.rest.fetch(`/public/v1/chat/${messageId}`, {
45
+ method: "DELETE",
46
+ });
47
+ }
48
+ }
@@ -0,0 +1,39 @@
1
+ import type { TokenManager } from "../auth/TokenManager.ts";
2
+
3
+ export class RestClient {
4
+ private tokenManager?: TokenManager;
5
+
6
+ setTokenManager(tokenManager: TokenManager) {
7
+ this.tokenManager = tokenManager;
8
+ }
9
+
10
+ async fetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
11
+ if (!this.tokenManager) {
12
+ throw new Error("TokenManager not set in RestClient");
13
+ }
14
+ const accessToken = await this.tokenManager.getAccessToken();
15
+
16
+ const response = await fetch(`https://api.kick.com${endpoint}`, {
17
+ ...options,
18
+ headers: {
19
+ Authorization: `Bearer ${accessToken}`,
20
+ "Content-Type": "application/json",
21
+ ...(options.headers || {}),
22
+ },
23
+ });
24
+
25
+ if (!response.ok) {
26
+ let suggestion = "";
27
+ if (response.status === 401) {
28
+ suggestion =
29
+ "Have you obtained access token or refreshed them? If so, make sure your scopes cover the requested endpoint.";
30
+ }
31
+
32
+ throw new Error(
33
+ `API request failed, response status: ${response.status}\n[SUGGESTION] ${suggestion}`,
34
+ );
35
+ }
36
+
37
+ return response.json() as Promise<T>;
38
+ }
39
+ }
@@ -0,0 +1,46 @@
1
+ import { logger } from "../Logger.ts";
2
+
3
+ type NgrokOptions = {
4
+ port?: number;
5
+ path?: string;
6
+ authtoken?: string;
7
+ domain?: string;
8
+ region?: string;
9
+ onUrl?: (url: string) => void;
10
+ };
11
+
12
+ export async function ngrokAdapter(options: NgrokOptions = {}) {
13
+ let ngrok: any;
14
+
15
+ try {
16
+ ngrok = await import("@ngrok/ngrok");
17
+ } catch {
18
+ throw new Error(
19
+ "@ngrok/ngrok is not installed. Install it with `npm install @ngrok/ngrok`.",
20
+ );
21
+ }
22
+
23
+ const port = options.port ?? 3000;
24
+ const path = options.path ?? "/kick/webhook";
25
+
26
+ const listener = await ngrok.connect({
27
+ addr: port,
28
+ domain: options.domain,
29
+ region: options.region,
30
+ authtoken: options.authtoken,
31
+ authtoken_from_env: !options.authtoken,
32
+ });
33
+
34
+ const fullUrl = `${listener.url()}${path}`;
35
+
36
+ options.onUrl?.(fullUrl);
37
+
38
+ logger.success("Ngrok tunnel established", { fullUrl });
39
+
40
+ return {
41
+ url: fullUrl,
42
+ async close() {
43
+ await listener.close();
44
+ },
45
+ };
46
+ }
@@ -0,0 +1,135 @@
1
+ import type { CreateServerOptions } from "../../types";
2
+ import { webhookServer } from "./WebhookServer.ts";
3
+ import { ngrokAdapter } from "./NgrokAdapter.ts";
4
+
5
+ const KICK_PUBLIC_KEY_PEM = `
6
+ -----BEGIN PUBLIC KEY-----
7
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq/+l1WnlRrGSolDMA+A8
8
+ 6rAhMbQGmQ2SapVcGM3zq8ANXjnhDWocMqfWcTd95btDydITa10kDvHzw9WQOqp2
9
+ MZI7ZyrfzJuz5nhTPCiJwTwnEtWft7nV14BYRDHvlfqPUaZ+1KR4OCaO/wWIk/rQ
10
+ L/TjY0M70gse8rlBkbo2a8rKhu69RQTRsoaf4DVhDPEeSeI5jVrRDGAMGL3cGuyY
11
+ 6CLKGdjVEM78g3JfYOvDU/RvfqD7L89TZ3iN94jrmWdGz34JNlEI5hqK8dd7C5EF
12
+ BEbZ5jgB8s8ReQV8H+MkuffjdAj3ajDDX3DOJMIut1lBrUVD1AaSrGCKHooWoL2e
13
+ twIDAQAB
14
+ -----END PUBLIC KEY-----
15
+ `.trim();
16
+
17
+ export async function verifyKickWebhookSignature(params: {
18
+ messageId: string;
19
+ timestamp: string;
20
+ rawBody: string;
21
+ signature: string;
22
+ }): Promise<boolean> {
23
+ const { messageId, timestamp, rawBody, signature } = params;
24
+ const payload = `${messageId}.${timestamp}.${rawBody}`;
25
+
26
+ const pemContents = KICK_PUBLIC_KEY_PEM.replace(
27
+ "-----BEGIN PUBLIC KEY-----",
28
+ "",
29
+ )
30
+ .replace("-----END PUBLIC KEY-----", "")
31
+ .replace(/\s+/g, "");
32
+
33
+ const binaryKey = Buffer.from(pemContents, "base64");
34
+
35
+ const publicKey = await crypto.subtle.importKey(
36
+ "spki",
37
+ binaryKey,
38
+ { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
39
+ false,
40
+ ["verify"],
41
+ );
42
+
43
+ const encoder = new TextEncoder();
44
+ const data = encoder.encode(payload);
45
+ const sigBuffer = Buffer.from(signature, "base64");
46
+
47
+ return await crypto.subtle.verify(
48
+ "RSASSA-PKCS1-v1_5",
49
+ publicKey,
50
+ sigBuffer,
51
+ data,
52
+ );
53
+ }
54
+
55
+ type Handler = (event: any, headers: Headers) => void | Promise<void>;
56
+
57
+ export class WebhookRouter {
58
+ private handlers = new Map<string, Set<Handler>>();
59
+
60
+ constructor(
61
+ private readonly secret: string,
62
+ private readonly rest: any,
63
+ ) {}
64
+
65
+ on(eventType: string, handler: Handler) {
66
+ if (!this.handlers.has(eventType)) {
67
+ this.handlers.set(eventType, new Set());
68
+ }
69
+ this.handlers.get(eventType)!.add(handler);
70
+ }
71
+
72
+ async handleRequest(params: {
73
+ rawBody: string;
74
+ headers: Record<string, string | string[] | undefined>;
75
+ }) {
76
+ const headers = params.headers;
77
+
78
+ const messageId = headers["kick-event-message-id"] as string;
79
+ const timestamp = headers["kick-event-message-timestamp"] as string;
80
+ const signature = headers["kick-event-signature"] as string;
81
+ const eventType = headers["kick-event-type"] as string;
82
+
83
+ if (!messageId || !timestamp || !signature || !eventType) {
84
+ throw new Error("Missing required Kick webhook headers");
85
+ }
86
+
87
+ const valid = await verifyKickWebhookSignature({
88
+ messageId,
89
+ timestamp,
90
+ rawBody: params.rawBody,
91
+ signature,
92
+ });
93
+
94
+ if (!valid) {
95
+ throw new Error("Invalid Kick webhook signature");
96
+ }
97
+
98
+ const payload = JSON.parse(params.rawBody);
99
+
100
+ const handlers = this.handlers.get(eventType);
101
+ if (!handlers) return;
102
+
103
+ for (const handler of handlers) {
104
+ await handler(payload, headers as any);
105
+ }
106
+ }
107
+
108
+ createServer(options: CreateServerOptions) {
109
+ return webhookServer(this, options);
110
+ }
111
+
112
+ async ngrok(options?: {
113
+ port?: number;
114
+ path?: string;
115
+ authtoken?: string;
116
+ domain?: string;
117
+ region?: string;
118
+ onUrl?: (url: string) => void;
119
+ }) {
120
+ return await ngrokAdapter(options);
121
+ }
122
+
123
+ async subscribe(options: { events: { name: string; version?: number }[] }) {
124
+ await this.rest.fetch("/public/v1/events/subscriptions", {
125
+ method: "POST",
126
+ body: JSON.stringify({
127
+ events: options.events.map((e) => ({
128
+ name: e.name,
129
+ version: e.version ?? 1,
130
+ })),
131
+ method: "webhook",
132
+ }),
133
+ });
134
+ }
135
+ }
@@ -0,0 +1,41 @@
1
+ import type { WebhookRouter } from "./WebhookRouter";
2
+ import type { CreateServerOptions } from "../../types";
3
+ import { logger } from "../Logger.ts";
4
+
5
+ export function webhookServer(
6
+ router: WebhookRouter,
7
+ options: CreateServerOptions = {},
8
+ ): Bun.Server<undefined> {
9
+ const port = options.port ?? 3000;
10
+ const path = options.path ?? "/kick/webhook";
11
+
12
+ const server = Bun.serve({
13
+ port,
14
+ async fetch(req) {
15
+ const url = new URL(req.url);
16
+
17
+ if (req.method !== "POST" || url.pathname !== path) {
18
+ return new Response(null, { status: 404 });
19
+ }
20
+
21
+ try {
22
+ const rawBody = await req.text();
23
+ const headers = Object.fromEntries(req.headers.entries());
24
+
25
+ await router.handleRequest({
26
+ rawBody,
27
+ headers,
28
+ });
29
+
30
+ return new Response("OK", { status: 200 });
31
+ } catch (err) {
32
+ logger.error(err);
33
+ return new Response("Unauthorized", { status: 401 });
34
+ }
35
+ },
36
+ });
37
+
38
+ logger.success("Webhook server started", { port, path });
39
+
40
+ return server;
41
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }
package/types/api.d.ts ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @type GetCategoriesParams
3
+ * @property {number?} [cursor] - The cursor for pagination. (Minimum: 4, Maximum: 28)
4
+ * @property {number?} [limit] - The maximum number of categories to return. (Minimum: 1, Maximum: 1000)
5
+ * @property {string[]?} [name] - Category names.
6
+ * @property {string[]?} [tags] - Tags associated with the categories.
7
+ * @property {number[]?} [id] - Category IDs.
8
+ */
9
+ export type GetCategoriesParams = {
10
+ cursor?: number;
11
+ limit?: number;
12
+ name?: string[];
13
+ tags?: string[];
14
+ id?: number[];
15
+ };
16
+
17
+ /**
18
+ * @interface KickChannel
19
+ * @property {string} slug - The unique identifier for the channel.
20
+ * @property {string} stream_title - The title of the current stream.
21
+ * @property {object} [category] - The category of the stream, if available.
22
+ * @property {number} category.id - The unique identifier for the category.
23
+ * @property {string} category.name - The name of the category.
24
+ */
25
+ export interface KickChannel {
26
+ slug: string;
27
+ stream_title: string;
28
+ category?: {
29
+ id: number;
30
+ name: string;
31
+ };
32
+ }
33
+
34
+ export interface ChannelRewardBase {
35
+ background_color?: string;
36
+ cost: number;
37
+ description: string;
38
+ is_enabled?: boolean;
39
+ is_user_input_required?: boolean;
40
+ should_redemptions_skip_request_queue?: boolean;
41
+ title: string;
42
+ }
43
+
44
+ export interface KickChannelReward extends ChannelRewardBase {
45
+ id: string;
46
+ is_paused: boolean;
47
+ }
48
+
49
+ export interface GetChannelRewardsResponse {
50
+ data: KickChannelReward[];
51
+ message: string;
52
+ }
53
+
54
+ export interface GetRedemptionsParams {
55
+ reward_id?: string;
56
+ status?: "pending" | "fulfilled" | "canceled";
57
+ id?: string[];
58
+ cursor?: string;
59
+ }
60
+
61
+ export interface KickRedemption {
62
+ id: string;
63
+ redeemed_at: string;
64
+ redeemer: {
65
+ user_id: number;
66
+ };
67
+ status: string;
68
+ user_input?: string;
69
+ }
70
+
71
+ export interface RedemptionData {
72
+ redemptions: KickRedemption[];
73
+ reward: {
74
+ can_manage: boolean;
75
+ cost: number;
76
+ description: string;
77
+ id: string;
78
+ is_deleted: boolean;
79
+ title: string;
80
+ };
81
+ }
82
+
83
+ export interface GetRedemptionsResponse {
84
+ data: RedemptionData[];
85
+ message: string;
86
+ pagination: {
87
+ next_cursor: string | null;
88
+ };
89
+ }
90
+
91
+ export interface RedemptionActionResponse {
92
+ data: { id: string; reason: string }[];
93
+ message: string;
94
+ }
95
+
96
+ export type KickOkResponse = {
97
+ data: Record<string, never> | {};
98
+ message: string;
99
+ };
100
+
101
+ export type ModerationBanRequest = {
102
+ broadcaster_user_id: number;
103
+ user_id: number;
104
+ reason?: string;
105
+ duration?: number;
106
+ };
107
+
108
+ export type ModerationUnbanRequest = {
109
+ broadcaster_user_id: number;
110
+ user_id: number;
111
+ };
112
+
113
+ export type KickLivestream = {
114
+ broadcaster_user_id: number;
115
+ channel_id: number;
116
+ slug: string;
117
+ stream_title: string;
118
+ viewer_count: number;
119
+ language: string;
120
+ has_mature_content: boolean;
121
+ started_at: string;
122
+ thumbnail: string;
123
+ profile_picture: string;
124
+ custom_tags: string[];
125
+ category: {
126
+ id: number;
127
+ name: string;
128
+ thumbnail: string;
129
+ };
130
+ };
131
+
132
+ export type KickLivestreamsResponse = {
133
+ data: KickLivestream[];
134
+ message: string;
135
+ };
136
+
137
+ export type KickLivestreamStatsResponse = {
138
+ data: {
139
+ total_count: number;
140
+ };
141
+ message: string;
142
+ };
143
+
144
+ export type KickLeaderboardEntry = {
145
+ gifted_amount: number;
146
+ rank: number;
147
+ user_id: number;
148
+ username: string;
149
+ };
150
+
151
+ export type KickLeaderboardResponse = {
152
+ data: {
153
+ lifetime: KickLeaderboardEntry[];
154
+ month: KickLeaderboardEntry[];
155
+ week: KickLeaderboardEntry[];
156
+ };
157
+ message: string;
158
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Represents the response received when getting or refreshing tokens from Kick.
3
+ *
4
+ * - `access_token`: The access token used for authentication.
5
+ * - `refresh_token`: The refresh token used to obtain new access tokens.
6
+ * - `token_type`: (Optional) The type of the token, typically "Bearer".
7
+ * - `expires_in`: (Optional) The duration in seconds until the access token expires.
8
+ * - `scope`: (Optional) The scopes granted to the access token.
9
+ *
10
+ * @interface KickTokenResponse
11
+ * @property {string} access_token - The access token used for authentication.
12
+ * @property {string} refresh_token - The refresh token used to obtain new access tokens.
13
+ * @property {string} [token_type] - (Optional) The type of the token, typically "Bearer".
14
+ * @property {number} [expires_in] - (Optional) The duration in seconds until the access token expires.
15
+ * @property {string} [scope] - (Optional) The scopes granted to the access token.
16
+ */
17
+ export interface KickTokenResponse {
18
+ access_token: string;
19
+ refresh_token: string;
20
+ token_type?: string;
21
+ expires_in?: number;
22
+ scope?: string;
23
+ }
24
+
25
+ /**
26
+ * Options for Kick authentication including initial tokens and token update callback.
27
+ *
28
+ * - `initialTokens`: (Optional) Initial tokens to set up the authentication.
29
+ * - `onTokenUpdate`: (Optional) Callback function invoked when tokens are updated.
30
+ *
31
+ * @interface KickAuthOptions
32
+ * @property {KickTokenResponse} [initialTokens] - (Optional) Initial tokens to set up the authentication.
33
+ * @property {(tokens: KickTokenResponse) => void | Promise<void>} [onTokenUpdate] - (Optional) Callback function invoked when tokens are updated.
34
+ */
35
+ export interface KickAuthOptions {
36
+ initialTokens?: KickTokenResponse;
37
+ onTokenUpdate?: (tokens: KickTokenResponse) => void | Promise<void>;
38
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Represents a message in a chat interface.
3
+ * - `content`: The text content of the message.
4
+ * - `type`: The type of message, either from a "bot" or a "user". Defaults to "bot" if not specified.
5
+ *
6
+ * @interface ChatMessage
7
+ * @property {string} content - The text content of the message.
8
+ * @property {"bot" | "user"} [type] - The type of message, either "bot" or "user".
9
+ *
10
+ */
11
+ export interface ChatMessage {
12
+ content: string;
13
+ type?: "bot" | "user";
14
+ }
@@ -0,0 +1,67 @@
1
+ import type { KickAuthOptions } from "./auth";
2
+
3
+ /**
4
+ * Available OAuth2 scopes for Kick API.
5
+ * @type {string}
6
+ */
7
+ type KickScopes =
8
+ | "user:read"
9
+ | "channel:read"
10
+ | "channel:write"
11
+ | "channel:rewards:read"
12
+ | "channel:rewards:write"
13
+ | "chat:write"
14
+ | "streamkey:read"
15
+ | "events:subscribe"
16
+ | "moderation:ban"
17
+ | "moderation:chat_message:manage"
18
+ | "kicks:read";
19
+
20
+ /**
21
+ * Options for initializing the KickClient.
22
+ *
23
+ * - `clientId`: The client ID of your Kick application.
24
+ * - `clientSecret`: The client secret of your Kick application.
25
+ * - `redirectUri`: The redirect URI for OAuth2 authorization.
26
+ * - `scopes`: The scopes required for your application.
27
+ * - `state`: Optional state parameter for OAuth2 authorization.
28
+ * - `auth`: Authentication options including token management.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const client = new KickClient({
33
+ * clientId: "your-client-id",
34
+ * clientSecret: "your-client-secret",
35
+ * redirectUri: "http://localhost:3000/callback",
36
+ * scopes: ["chat:read", "chat:write"],
37
+ * auth: {
38
+ * initialTokens: {
39
+ * access_token: "initial-access-token",
40
+ * refresh_token: "initial-refresh-token",
41
+ * expires_in: 7200,
42
+ * scope: "chat:read chat:write",
43
+ * token_type: "Bearer",
44
+ * },
45
+ * },
46
+ * onTokenUpdate: (tokens) => {
47
+ * console.log("Tokens updated:", tokens);
48
+ * }
49
+ * });
50
+ * ```
51
+ *
52
+ * @interface KickClientOptions
53
+ * @property {string} clientId - The client ID of your Kick application.
54
+ * @property {string} clientSecret - The client secret of your Kick application.
55
+ * @property {string} redirectUri - The redirect URI for OAuth2 authorization.
56
+ * @property {KickScopes[]} scopes - The scopes required for your application.
57
+ * @property {string} [state] - Optional state parameter for OAuth2 authorization.
58
+ * @property {KickAuthOptions} [auth] - Authentication options including token management.
59
+ */
60
+ export interface KickClientOptions {
61
+ clientId: string;
62
+ clientSecret: string;
63
+ redirectUri: string;
64
+ scopes: KickScopes[];
65
+ state?: string;
66
+ auth?: KickAuthOptions;
67
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./client";
2
+ export * from "./chat";
3
+ export * from "./auth";
4
+ export * from "./webhooks";
@@ -0,0 +1,35 @@
1
+ export interface KickWebhookEvent<T = any> {
2
+ event: string;
3
+ data: T;
4
+ created_at: string;
5
+ }
6
+
7
+ export interface ChatMessageEvent {
8
+ message_id: string;
9
+ replies_to: string | null;
10
+ broadcaster: {
11
+ is_anonymous: boolean;
12
+ user_id: number;
13
+ username: string;
14
+ is_verified: boolean;
15
+ profile_picture: string;
16
+ channel_slug: string;
17
+ identity: any | null;
18
+ };
19
+ sender: {
20
+ is_anonymous: boolean;
21
+ user_id: number;
22
+ username: string;
23
+ is_verified: boolean;
24
+ profile_picture: string;
25
+ channel_slug: string;
26
+ identity: any | null;
27
+ };
28
+ content: string;
29
+ emotes: any[];
30
+ created_at: string;
31
+ }
32
+ export interface CreateServerOptions {
33
+ port?: number;
34
+ path?: string;
35
+ }