tentacle-sdk 0.0.1

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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +169 -0
  3. package/dist/index.cjs +889 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +1562 -0
  6. package/dist/index.d.ts +1562 -0
  7. package/dist/index.js +884 -0
  8. package/dist/index.js.map +1 -0
  9. package/docs/api/classes/DeviceAuthFlow.md +107 -0
  10. package/docs/api/classes/FileCredentialStore.md +95 -0
  11. package/docs/api/classes/TentacleClient.md +118 -0
  12. package/docs/api/classes/TentacleError.md +69 -0
  13. package/docs/api/index.md +117 -0
  14. package/docs/api/interfaces/AppJson.md +20 -0
  15. package/docs/api/interfaces/ChatMessageEmoji.md +16 -0
  16. package/docs/api/interfaces/ChatMessageEmote.md +16 -0
  17. package/docs/api/interfaces/ChatMessagePosition.md +14 -0
  18. package/docs/api/interfaces/CommonChatMessage.md +27 -0
  19. package/docs/api/interfaces/CommonEventJson.md +32 -0
  20. package/docs/api/interfaces/CredentialStore.md +55 -0
  21. package/docs/api/interfaces/DeviceAuthInitResult.md +17 -0
  22. package/docs/api/interfaces/DeviceAuthOptions.md +18 -0
  23. package/docs/api/interfaces/DeviceAuthPollResult.md +14 -0
  24. package/docs/api/interfaces/DonationUnlock.md +16 -0
  25. package/docs/api/interfaces/FileCredentialStoreOptions.md +13 -0
  26. package/docs/api/interfaces/GiftedSubUnlock.md +15 -0
  27. package/docs/api/interfaces/KickBadge.md +15 -0
  28. package/docs/api/interfaces/KickChatMessageJson.md +35 -0
  29. package/docs/api/interfaces/KickEventJson_ChannelFollow.md +25 -0
  30. package/docs/api/interfaces/KickEventJson_ChannelSubscriptionGifts.md +25 -0
  31. package/docs/api/interfaces/KickEventJson_ChannelSubscriptionNew.md +25 -0
  32. package/docs/api/interfaces/KickEventJson_ChannelSubscriptionRenewal.md +25 -0
  33. package/docs/api/interfaces/KickEventJson_LivestreamStatusUpdated.md +24 -0
  34. package/docs/api/interfaces/RealtimeEvent_StreamChatMessage.md +14 -0
  35. package/docs/api/interfaces/RealtimeEvent_StreamEvent.md +14 -0
  36. package/docs/api/interfaces/RealtimeEvent_StreamViewerActivity.md +14 -0
  37. package/docs/api/interfaces/RealtimeSubscribeOptions.md +16 -0
  38. package/docs/api/interfaces/StreamViewerActivityJson.md +16 -0
  39. package/docs/api/interfaces/SubUnlock.md +15 -0
  40. package/docs/api/interfaces/SubathonStats_Donations.md +23 -0
  41. package/docs/api/interfaces/SubathonStats_GiftedSubscriptions.md +22 -0
  42. package/docs/api/interfaces/SubathonStats_Subscriptions.md +20 -0
  43. package/docs/api/interfaces/TentacleClientConfig.md +14 -0
  44. package/docs/api/interfaces/TentacleClientCreateOptions.md +15 -0
  45. package/docs/api/interfaces/TwitchChatMessageJson.md +41 -0
  46. package/docs/api/interfaces/TwitchEventJson_Cheer.md +29 -0
  47. package/docs/api/interfaces/TwitchEventJson_Follow.md +27 -0
  48. package/docs/api/interfaces/TwitchEventJson_Raid.md +27 -0
  49. package/docs/api/interfaces/TwitchEventJson_RedemptionAdd.md +34 -0
  50. package/docs/api/interfaces/TwitchEventJson_StreamOnline.md +26 -0
  51. package/docs/api/interfaces/TwitchEventJson_Subscription.md +28 -0
  52. package/docs/api/interfaces/TwitchEventJson_SubscriptionGift.md +30 -0
  53. package/docs/api/interfaces/TwitchUserInfo.md +24 -0
  54. package/docs/api/interfaces/ViewerActionJson.md +18 -0
  55. package/docs/api/interfaces/ViewerJson.md +23 -0
  56. package/docs/api/interfaces/ViewerKickJson.md +25 -0
  57. package/docs/api/interfaces/ViewerMiniJson.md +21 -0
  58. package/docs/api/interfaces/ViewerPropertyJsonBase.md +22 -0
  59. package/docs/api/interfaces/ViewerPropertyJson_Bool.md +22 -0
  60. package/docs/api/interfaces/ViewerPropertyJson_Number.md +22 -0
  61. package/docs/api/interfaces/ViewerPropertyJson_String.md +22 -0
  62. package/docs/api/interfaces/ViewerTwitchJson.md +24 -0
  63. package/docs/api/interfaces/ViewersByPropertyOutput.md +17 -0
  64. package/docs/api/type-aliases/DateIsoString.md +9 -0
  65. package/docs/api/type-aliases/KickEventJson.md +9 -0
  66. package/docs/api/type-aliases/OrderDirection.md +9 -0
  67. package/docs/api/type-aliases/RealtimeEvent.md +36 -0
  68. package/docs/api/type-aliases/StreamChatMessageJson.md +9 -0
  69. package/docs/api/type-aliases/StreamEventJson.md +9 -0
  70. package/docs/api/type-aliases/StreamPlatform.md +9 -0
  71. package/docs/api/type-aliases/TwitchEventJson.md +9 -0
  72. package/docs/api/type-aliases/TwitchEventType.md +9 -0
  73. package/docs/api/type-aliases/UnsubscribeFunction.md +22 -0
  74. package/docs/api/type-aliases/ViewerPropertyJson.md +9 -0
  75. package/docs/api/type-aliases/ViewerPropertyType.md +9 -0
  76. package/docs/overview.md +160 -0
  77. package/package.json +54 -0
@@ -0,0 +1,1562 @@
1
+ /**
2
+ * HTTP utilities for the Tentacle SDK.
3
+ * Uses tRPC HTTP protocol to communicate with the Tentacle API.
4
+ *
5
+ * @module http
6
+ */
7
+ /**
8
+ * Error thrown by the Tentacle SDK when an API request fails.
9
+ */
10
+ declare class TentacleError extends Error {
11
+ /**
12
+ * HTTP status code of the failed request.
13
+ */
14
+ readonly status: number;
15
+ /**
16
+ * Error code from the server, if available.
17
+ */
18
+ readonly code?: string;
19
+ constructor(message: string, status: number, code?: string);
20
+ }
21
+ /**
22
+ * Configuration for the HTTP client.
23
+ */
24
+ interface HttpClientConfig {
25
+ /**
26
+ * Base URL of the Tentacle API.
27
+ * @example "https://api.tentacle.live"
28
+ */
29
+ baseUrl: string;
30
+ /**
31
+ * Access token for authentication.
32
+ * Obtain this from the Tentacle dashboard or via the auth API.
33
+ */
34
+ accessToken?: string;
35
+ }
36
+ /**
37
+ * HTTP client for making tRPC requests to the Tentacle API.
38
+ */
39
+ declare class HttpClient {
40
+ private readonly baseUrl;
41
+ private readonly accessToken?;
42
+ constructor(config: HttpClientConfig);
43
+ /**
44
+ * Get the headers for a request.
45
+ */
46
+ private getHeaders;
47
+ /**
48
+ * Build the tRPC URL for a procedure call.
49
+ *
50
+ * @param procedure - Full procedure path (e.g., "apps.list")
51
+ * @param input - Optional input for queries (will be URL encoded)
52
+ */
53
+ private buildTrpcUrl;
54
+ /**
55
+ * Handle the tRPC response.
56
+ */
57
+ private handleResponse;
58
+ /**
59
+ * Make a tRPC query (GET request).
60
+ *
61
+ * @param procedure - Full procedure path (e.g., "apps.list")
62
+ * @param input - Optional input data
63
+ * @returns The response data
64
+ * @throws {TentacleError} If the request fails
65
+ */
66
+ query<T>(procedure: string, input?: unknown): Promise<T>;
67
+ /**
68
+ * Make a tRPC mutation (POST request).
69
+ *
70
+ * @param procedure - Full procedure path (e.g., "apps.create")
71
+ * @param input - Input data for the mutation
72
+ * @returns The response data
73
+ * @throws {TentacleError} If the request fails
74
+ */
75
+ mutate<T>(procedure: string, input?: unknown): Promise<T>;
76
+ /**
77
+ * Get the base URL for SSE connections.
78
+ */
79
+ getBaseUrl(): string;
80
+ /**
81
+ * Get the access token for SSE connections.
82
+ */
83
+ getAccessToken(): string | undefined;
84
+ }
85
+
86
+ /**
87
+ * Type definitions for the Tentacle SDK.
88
+ *
89
+ * These types mirror the server's Zod schemas to ensure type safety.
90
+ * All types are defined inline to make the SDK self-contained.
91
+ *
92
+ * @module types
93
+ */
94
+ /**
95
+ * ISO 8601 date string.
96
+ */
97
+ type DateIsoString = string;
98
+ /**
99
+ * Stream platform.
100
+ */
101
+ type StreamPlatform = "twitch" | "kick";
102
+ /**
103
+ * Position of an emote or emoji in a chat message.
104
+ */
105
+ interface ChatMessagePosition {
106
+ start: number;
107
+ end: number;
108
+ }
109
+ /**
110
+ * Emoji in a chat message.
111
+ */
112
+ interface ChatMessageEmoji {
113
+ code: string;
114
+ name: string;
115
+ count: number;
116
+ positions: ChatMessagePosition[];
117
+ }
118
+ /**
119
+ * Emote in a chat message.
120
+ */
121
+ interface ChatMessageEmote {
122
+ id: string;
123
+ name: string;
124
+ count: number;
125
+ positions: ChatMessagePosition[];
126
+ }
127
+ /**
128
+ * Base fields for all chat messages.
129
+ */
130
+ interface CommonChatMessage {
131
+ $command: string | null;
132
+ $commandArgs: string[];
133
+ $createdAt: DateIsoString;
134
+ $emojis: Record<string, ChatMessageEmoji>;
135
+ $emotes: Record<string, ChatMessageEmote>;
136
+ $html: string;
137
+ $id: string;
138
+ $kind: "ChatMessageJson";
139
+ $text: string;
140
+ $viewerId: string;
141
+ }
142
+ /**
143
+ * Twitch chat message.
144
+ */
145
+ interface TwitchChatMessageJson extends CommonChatMessage {
146
+ $platform: "twitch";
147
+ $type: "chat.message";
148
+ bits: number;
149
+ channelId: string;
150
+ color: string | null;
151
+ emoteOnly: boolean;
152
+ id: string;
153
+ isCheer: boolean;
154
+ isFirst: boolean;
155
+ isHighlight: boolean;
156
+ isRedemption: boolean;
157
+ isReply: boolean;
158
+ isReturningChatter: boolean;
159
+ message: string;
160
+ userInfo: TwitchUserInfo | null;
161
+ }
162
+ /**
163
+ * Twitch user info.
164
+ */
165
+ interface TwitchUserInfo {
166
+ badgeInfo: Record<string, string>;
167
+ badges: Record<string, string>;
168
+ color: string | null;
169
+ displayName: string;
170
+ id: string;
171
+ isBroadcaster: boolean;
172
+ isFounder: boolean;
173
+ isMod: boolean;
174
+ isSubscriber: boolean;
175
+ isVip: boolean;
176
+ name: string;
177
+ userType: string | null;
178
+ }
179
+ /**
180
+ * Kick chat message.
181
+ */
182
+ interface KickChatMessageJson extends CommonChatMessage {
183
+ $platform: "kick";
184
+ $type: "chat.message";
185
+ chatroomId: number;
186
+ content: string;
187
+ createdAt: string;
188
+ id: string;
189
+ senderId: number;
190
+ type: string;
191
+ username: string;
192
+ }
193
+ /**
194
+ * Chat message from Twitch or Kick.
195
+ */
196
+ type StreamChatMessageJson = TwitchChatMessageJson | KickChatMessageJson;
197
+ /**
198
+ * Base fields for all stream events.
199
+ */
200
+ interface CommonEventJson {
201
+ $createdAt: DateIsoString;
202
+ $id: string;
203
+ $kind: "EventJson";
204
+ $userId: string;
205
+ $viewerId: string | null;
206
+ }
207
+ /**
208
+ * Twitch event types.
209
+ */
210
+ type TwitchEventType = "channel.ban" | "channel.cheer" | "channel.follow" | "channel.raid" | "channel.channel_points_custom_reward_redemption.add" | "channel.shield_mode.begin" | "channel.shield_mode.end" | "channel.shoutout.receive" | "channel.subscribe" | "channel.subscription.gift" | "channel.subscription.message" | "stream.online" | "stream.offline";
211
+ /**
212
+ * Twitch follow event.
213
+ */
214
+ interface TwitchEventJson_Follow extends CommonEventJson {
215
+ $platform: "twitch";
216
+ $type: "channel.follow";
217
+ followedAt: string;
218
+ userId: string;
219
+ userDisplayName: string;
220
+ userName: string;
221
+ }
222
+ /**
223
+ * Twitch subscription event.
224
+ */
225
+ interface TwitchEventJson_Subscription extends CommonEventJson {
226
+ $platform: "twitch";
227
+ $type: "channel.subscribe";
228
+ isGift: boolean;
229
+ tier: string;
230
+ userId: string;
231
+ userDisplayName: string;
232
+ userName: string;
233
+ }
234
+ /**
235
+ * Twitch subscription gift event.
236
+ */
237
+ interface TwitchEventJson_SubscriptionGift extends CommonEventJson {
238
+ $platform: "twitch";
239
+ $type: "channel.subscription.gift";
240
+ cumulativeTotal: number | null;
241
+ isAnonymous: boolean;
242
+ tier: string;
243
+ total: number;
244
+ userId: string | null;
245
+ userDisplayName: string | null;
246
+ userName: string | null;
247
+ }
248
+ /**
249
+ * Twitch cheer event.
250
+ */
251
+ interface TwitchEventJson_Cheer extends CommonEventJson {
252
+ $platform: "twitch";
253
+ $type: "channel.cheer";
254
+ bits: number;
255
+ isAnonymous: boolean;
256
+ message: string;
257
+ userId: string | null;
258
+ userDisplayName: string | null;
259
+ userName: string | null;
260
+ }
261
+ /**
262
+ * Twitch raid event.
263
+ */
264
+ interface TwitchEventJson_Raid extends CommonEventJson {
265
+ $platform: "twitch";
266
+ $type: "channel.raid";
267
+ fromBroadcasterUserId: string;
268
+ fromBroadcasterUserName: string;
269
+ fromBroadcasterUserDisplayName: string;
270
+ viewers: number;
271
+ }
272
+ /**
273
+ * Twitch channel point redemption event.
274
+ */
275
+ interface TwitchEventJson_RedemptionAdd extends CommonEventJson {
276
+ $platform: "twitch";
277
+ $type: "channel.channel_points_custom_reward_redemption.add";
278
+ id: string;
279
+ rewardId: string;
280
+ rewardTitle: string;
281
+ rewardCost: number;
282
+ rewardPrompt: string;
283
+ userId: string;
284
+ userDisplayName: string;
285
+ userName: string;
286
+ userInput: string;
287
+ status: string;
288
+ redeemedAt: string;
289
+ }
290
+ /**
291
+ * Twitch stream online event.
292
+ */
293
+ interface TwitchEventJson_StreamOnline extends CommonEventJson {
294
+ $platform: "twitch";
295
+ $type: "stream.online";
296
+ id: string;
297
+ type: string;
298
+ startedAt: string;
299
+ }
300
+ /**
301
+ * All Twitch event types.
302
+ */
303
+ type TwitchEventJson = TwitchEventJson_Follow | TwitchEventJson_Subscription | TwitchEventJson_SubscriptionGift | TwitchEventJson_Cheer | TwitchEventJson_Raid | TwitchEventJson_RedemptionAdd | TwitchEventJson_StreamOnline;
304
+ /**
305
+ * Kick follow event.
306
+ */
307
+ interface KickEventJson_ChannelFollow extends CommonEventJson {
308
+ $platform: "kick";
309
+ $type: "channel.followed";
310
+ followersCount: number;
311
+ username: string;
312
+ }
313
+ /**
314
+ * Kick subscription new event.
315
+ */
316
+ interface KickEventJson_ChannelSubscriptionNew extends CommonEventJson {
317
+ $platform: "kick";
318
+ $type: "channel.subscription.new";
319
+ months: number;
320
+ username: string;
321
+ }
322
+ /**
323
+ * Kick subscription renewal event.
324
+ */
325
+ interface KickEventJson_ChannelSubscriptionRenewal extends CommonEventJson {
326
+ $platform: "kick";
327
+ $type: "channel.subscription.renewal";
328
+ months: number;
329
+ username: string;
330
+ }
331
+ /**
332
+ * Kick subscription gifts event.
333
+ */
334
+ interface KickEventJson_ChannelSubscriptionGifts extends CommonEventJson {
335
+ $platform: "kick";
336
+ $type: "channel.subscription.gifts";
337
+ giftedUsernames: string[];
338
+ gifterUsername: string;
339
+ }
340
+ /**
341
+ * Kick livestream status updated event.
342
+ */
343
+ interface KickEventJson_LivestreamStatusUpdated extends CommonEventJson {
344
+ $platform: "kick";
345
+ $type: "livestream.status.updated";
346
+ isLive: boolean;
347
+ }
348
+ /**
349
+ * All Kick event types.
350
+ */
351
+ type KickEventJson = KickEventJson_ChannelFollow | KickEventJson_ChannelSubscriptionNew | KickEventJson_ChannelSubscriptionRenewal | KickEventJson_ChannelSubscriptionGifts | KickEventJson_LivestreamStatusUpdated;
352
+ /**
353
+ * Stream event from Twitch or Kick.
354
+ */
355
+ type StreamEventJson = TwitchEventJson | KickEventJson;
356
+ /**
357
+ * Viewer activity event.
358
+ */
359
+ interface StreamViewerActivityJson {
360
+ id: string;
361
+ userId: string;
362
+ viewerId: string;
363
+ createdAt: DateIsoString;
364
+ }
365
+ /**
366
+ * Realtime chat message event.
367
+ */
368
+ interface RealtimeEvent_StreamChatMessage {
369
+ kind: "StreamChatMessage";
370
+ payload: StreamChatMessageJson;
371
+ }
372
+ /**
373
+ * Realtime stream event.
374
+ */
375
+ interface RealtimeEvent_StreamEvent {
376
+ kind: "StreamEvent";
377
+ payload: StreamEventJson;
378
+ }
379
+ /**
380
+ * Realtime viewer activity event.
381
+ */
382
+ interface RealtimeEvent_StreamViewerActivity {
383
+ kind: "StreamViewerActivity";
384
+ payload: StreamViewerActivityJson;
385
+ }
386
+ /**
387
+ * Realtime event from the Tentacle server.
388
+ *
389
+ * This is a discriminated union - use `event.kind` to determine the event type:
390
+ * - `StreamChatMessage`: Chat message from Twitch or Kick
391
+ * - `StreamEvent`: Stream event (follow, sub, raid, cheer, etc.)
392
+ * - `StreamViewerActivity`: Viewer activity update
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * client.realtime.subscribe({
397
+ * onEvent: (event) => {
398
+ * switch (event.kind) {
399
+ * case 'StreamChatMessage':
400
+ * console.log(`[${event.payload.$platform}] ${event.payload.$text}`)
401
+ * break
402
+ * case 'StreamEvent':
403
+ * if (event.payload.$platform === 'twitch' && event.payload.$type === 'channel.follow') {
404
+ * console.log(`New follower: ${event.payload.userDisplayName}`)
405
+ * }
406
+ * break
407
+ * case 'StreamViewerActivity':
408
+ * console.log(`Viewer ${event.payload.viewerId} active`)
409
+ * break
410
+ * }
411
+ * }
412
+ * })
413
+ * ```
414
+ */
415
+ type RealtimeEvent = RealtimeEvent_StreamChatMessage | RealtimeEvent_StreamEvent | RealtimeEvent_StreamViewerActivity;
416
+ /**
417
+ * App.
418
+ */
419
+ interface AppJson {
420
+ createdAt: DateIsoString;
421
+ id: string;
422
+ isActive: boolean;
423
+ isDefault: boolean;
424
+ name: string;
425
+ state: unknown;
426
+ updatedAt: DateIsoString;
427
+ userId: string;
428
+ }
429
+ /**
430
+ * Viewer property type.
431
+ */
432
+ type ViewerPropertyType = "number" | "bool" | "string";
433
+ /**
434
+ * Base viewer property fields.
435
+ */
436
+ interface ViewerPropertyJsonBase {
437
+ id: string;
438
+ appId: string;
439
+ name: string;
440
+ viewerId: string;
441
+ }
442
+ /**
443
+ * Number viewer property.
444
+ */
445
+ interface ViewerPropertyJson_Number extends ViewerPropertyJsonBase {
446
+ type: "number";
447
+ value: number;
448
+ }
449
+ /**
450
+ * Boolean viewer property.
451
+ */
452
+ interface ViewerPropertyJson_Bool extends ViewerPropertyJsonBase {
453
+ type: "bool";
454
+ value: boolean;
455
+ }
456
+ /**
457
+ * String viewer property.
458
+ */
459
+ interface ViewerPropertyJson_String extends ViewerPropertyJsonBase {
460
+ type: "string";
461
+ value: string;
462
+ }
463
+ /**
464
+ * Viewer property.
465
+ */
466
+ type ViewerPropertyJson = ViewerPropertyJson_Number | ViewerPropertyJson_Bool | ViewerPropertyJson_String;
467
+ /**
468
+ * Kick badge.
469
+ */
470
+ interface KickBadge {
471
+ type: string;
472
+ text: string;
473
+ count?: number;
474
+ }
475
+ /**
476
+ * Viewer Kick data.
477
+ */
478
+ interface ViewerKickJson {
479
+ createdAt: DateIsoString;
480
+ updatedAt: DateIsoString;
481
+ viewerId: string;
482
+ id: number;
483
+ username: string;
484
+ badges?: KickBadge[] | null;
485
+ isBroadcaster: boolean;
486
+ isModerator: boolean;
487
+ isOwner: boolean;
488
+ isStaff: boolean;
489
+ isSubscriber: boolean;
490
+ isVerified: boolean;
491
+ isVip: boolean;
492
+ }
493
+ /**
494
+ * Viewer Twitch data.
495
+ */
496
+ interface ViewerTwitchJson {
497
+ createdAt: DateIsoString;
498
+ updatedAt: DateIsoString;
499
+ viewerId: string;
500
+ id: string;
501
+ username: string;
502
+ displayName: string;
503
+ profileImageUrl?: string | null;
504
+ isBroadcaster: boolean;
505
+ isFounder: boolean;
506
+ isMod: boolean;
507
+ isSubscriber: boolean;
508
+ isVip: boolean;
509
+ }
510
+ /**
511
+ * Viewer (mini).
512
+ */
513
+ interface ViewerMiniJson {
514
+ id: string;
515
+ createdAt: DateIsoString;
516
+ updatedAt: DateIsoString;
517
+ kick?: ViewerKickJson | null;
518
+ twitch?: ViewerTwitchJson | null;
519
+ }
520
+ /**
521
+ * Viewer (full).
522
+ */
523
+ interface ViewerJson extends ViewerMiniJson {
524
+ isMock?: boolean;
525
+ properties?: Record<string, ViewerPropertyJson> | null;
526
+ }
527
+ /**
528
+ * Viewer action.
529
+ */
530
+ interface ViewerActionJson {
531
+ id: string;
532
+ name: string;
533
+ data: unknown;
534
+ appId: string;
535
+ viewerId: string;
536
+ createdAt: DateIsoString;
537
+ }
538
+ /**
539
+ * Subscription unlock.
540
+ */
541
+ interface SubUnlock {
542
+ subs: number;
543
+ title: string;
544
+ reached: boolean;
545
+ }
546
+ /**
547
+ * Subscription stats.
548
+ */
549
+ interface SubathonStats_Subscriptions {
550
+ count: number;
551
+ goal: number;
552
+ goalProgress: number;
553
+ newestSubViewers: ViewerJson[];
554
+ unlocks: SubUnlock[];
555
+ nextUnlock: {
556
+ subs: number;
557
+ title: string;
558
+ };
559
+ }
560
+ /**
561
+ * Gifted subscription unlock.
562
+ */
563
+ interface GiftedSubUnlock {
564
+ giftedSubs: number;
565
+ title: string;
566
+ reached: boolean;
567
+ }
568
+ /**
569
+ * Gifted subscription stats.
570
+ */
571
+ interface SubathonStats_GiftedSubscriptions {
572
+ count: number;
573
+ goal: number;
574
+ goalProgress: number;
575
+ latestGiftedSub: unknown;
576
+ topGifterViewers: ViewerJson[];
577
+ viewerIdToTotal: Record<string, number>;
578
+ unlocks: GiftedSubUnlock[];
579
+ nextUnlock: {
580
+ giftedSubs: number;
581
+ title: string;
582
+ };
583
+ }
584
+ /**
585
+ * Donation unlock.
586
+ */
587
+ interface DonationUnlock {
588
+ dollars: number;
589
+ dollarsText: string;
590
+ title: string;
591
+ reached: boolean;
592
+ }
593
+ /**
594
+ * Donation stats.
595
+ */
596
+ interface SubathonStats_Donations {
597
+ dollars: number;
598
+ goal: number;
599
+ goalProgress: number;
600
+ latestCheer: unknown;
601
+ topDonorViewers: ViewerJson[];
602
+ viewerIdToTotalBits: Record<string, number>;
603
+ unlocks: DonationUnlock[];
604
+ nextUnlock: {
605
+ dollars: number;
606
+ dollarsText: string;
607
+ title: string;
608
+ };
609
+ }
610
+ /**
611
+ * Order direction for pagination.
612
+ */
613
+ type OrderDirection = "asc" | "desc";
614
+ /**
615
+ * Viewers by property response.
616
+ */
617
+ interface ViewersByPropertyOutput {
618
+ property: {
619
+ name: string;
620
+ type: ViewerPropertyType;
621
+ appId: string;
622
+ };
623
+ viewers: Array<ViewerMiniJson & {
624
+ properties: Record<string, ViewerPropertyJson>;
625
+ }>;
626
+ }
627
+
628
+ /**
629
+ * Realtime subscription utilities for the Tentacle SDK.
630
+ * Uses Server-Sent Events (SSE) via tRPC to receive live updates from Twitch and Kick.
631
+ *
632
+ * @module realtime
633
+ */
634
+
635
+ /**
636
+ * Options for subscribing to realtime events.
637
+ */
638
+ interface RealtimeSubscribeOptions {
639
+ /**
640
+ * Callback invoked when a realtime event is received.
641
+ * Events include chat messages, stream events (follows, subs, raids, etc.),
642
+ * and viewer activity.
643
+ *
644
+ * @param event - The realtime event with `kind` discriminator and `payload`.
645
+ *
646
+ * @example
647
+ * ```typescript
648
+ * onEvent: (event) => {
649
+ * switch (event.kind) {
650
+ * case 'StreamChatMessage':
651
+ * console.log(`${event.payload.userInfo?.displayName}: ${event.payload.$text}`)
652
+ * break
653
+ * case 'StreamEvent':
654
+ * if (event.payload.$platform === 'twitch' && event.payload.$type === 'channel.follow') {
655
+ * console.log(`New follower: ${event.payload.userDisplayName}`)
656
+ * }
657
+ * break
658
+ * }
659
+ * }
660
+ * ```
661
+ */
662
+ onEvent: (event: RealtimeEvent) => void;
663
+ /**
664
+ * Callback invoked when an error occurs.
665
+ * The subscription will automatically attempt to reconnect.
666
+ *
667
+ * @param error - The error that occurred.
668
+ */
669
+ onError?: (error: Error) => void;
670
+ /**
671
+ * Callback invoked when the connection is established.
672
+ */
673
+ onOpen?: () => void;
674
+ /**
675
+ * Callback invoked when the connection is closed.
676
+ */
677
+ onClose?: () => void;
678
+ }
679
+ /**
680
+ * Function to unsubscribe from realtime events.
681
+ * Call this to close the SSE connection and stop receiving events.
682
+ *
683
+ * @example
684
+ * ```typescript
685
+ * const unsubscribe = client.realtime.subscribe({ onEvent: ... })
686
+ * // Later, when done:
687
+ * unsubscribe()
688
+ * ```
689
+ */
690
+ type UnsubscribeFunction = () => void;
691
+ /**
692
+ * Realtime API for subscribing to Twitch and Kick events.
693
+ */
694
+ declare class RealtimeApi {
695
+ private readonly baseUrl;
696
+ private readonly accessToken?;
697
+ constructor(baseUrl: string, accessToken?: string);
698
+ /**
699
+ * Subscribe to realtime events from Twitch and Kick.
700
+ *
701
+ * This establishes a Server-Sent Events (SSE) connection to receive live updates:
702
+ * - **StreamChatMessage**: Chat messages from Twitch or Kick
703
+ * - **StreamEvent**: Events like follows, subscriptions, raids, cheers
704
+ * - **StreamViewerActivity**: Viewer activity updates
705
+ *
706
+ * Each event includes viewer data (when available) via the `$viewerId` field
707
+ * and platform-specific user info.
708
+ *
709
+ * @param options - Subscription options with event callbacks
710
+ * @returns Function to unsubscribe and close the connection
711
+ *
712
+ * @example
713
+ * ```typescript
714
+ * const unsubscribe = client.realtime.subscribe({
715
+ * onEvent: (event) => {
716
+ * switch (event.kind) {
717
+ * case 'StreamChatMessage':
718
+ * // Chat message from Twitch or Kick
719
+ * const msg = event.payload
720
+ * console.log(`[${msg.$platform}] ${msg.$text}`)
721
+ * console.log(`Viewer ID: ${msg.$viewerId}`)
722
+ * break
723
+ *
724
+ * case 'StreamEvent':
725
+ * // Stream event (follow, sub, raid, etc.)
726
+ * const evt = event.payload
727
+ * if (evt.$platform === 'twitch') {
728
+ * switch (evt.$type) {
729
+ * case 'channel.follow':
730
+ * console.log(`New Twitch follower: ${evt.userDisplayName}`)
731
+ * break
732
+ * case 'channel.subscribe':
733
+ * console.log(`New Twitch sub: ${evt.userDisplayName}`)
734
+ * break
735
+ * case 'channel.raid':
736
+ * console.log(`Raid from ${evt.fromBroadcasterUserName} with ${evt.viewers} viewers`)
737
+ * break
738
+ * }
739
+ * } else if (evt.$platform === 'kick') {
740
+ * switch (evt.$type) {
741
+ * case 'channel.followed':
742
+ * console.log(`New Kick follower: ${evt.username}`)
743
+ * break
744
+ * case 'channel.subscription.new':
745
+ * console.log(`New Kick sub: ${evt.username}`)
746
+ * break
747
+ * }
748
+ * }
749
+ * break
750
+ *
751
+ * case 'StreamViewerActivity':
752
+ * console.log(`Viewer activity: ${event.payload.viewerId}`)
753
+ * break
754
+ * }
755
+ * },
756
+ * onError: (error) => console.error('Connection error:', error),
757
+ * onOpen: () => console.log('Connected!'),
758
+ * onClose: () => console.log('Disconnected'),
759
+ * })
760
+ *
761
+ * // When done, unsubscribe:
762
+ * unsubscribe()
763
+ * ```
764
+ *
765
+ * @throws {Error} If no access token is configured
766
+ */
767
+ subscribe(options: RealtimeSubscribeOptions): UnsubscribeFunction;
768
+ }
769
+
770
+ /**
771
+ * Credential store interface for persisting access tokens.
772
+ *
773
+ * Implement this interface to provide custom credential storage.
774
+ * The SDK includes a FileCredentialStore for Node.js environments.
775
+ *
776
+ * @example
777
+ * ```typescript
778
+ * // Custom credential store for Unreal Engine (PuerTS)
779
+ * class UnrealCredentialStore implements CredentialStore {
780
+ * async getAccessToken(): Promise<string | null> {
781
+ * return UE.SaveGame.LoadString("TentacleAccessToken")
782
+ * }
783
+ *
784
+ * async setAccessToken(token: string): Promise<void> {
785
+ * UE.SaveGame.SaveString("TentacleAccessToken", token)
786
+ * }
787
+ *
788
+ * async clearAccessToken(): Promise<void> {
789
+ * UE.SaveGame.Delete("TentacleAccessToken")
790
+ * }
791
+ * }
792
+ * ```
793
+ *
794
+ * @module auth/credential-store
795
+ */
796
+ /**
797
+ * Interface for persisting access tokens.
798
+ *
799
+ * Implementations should securely store tokens in a platform-appropriate way.
800
+ * For Node.js, use FileCredentialStore. For other platforms (Unity, Unreal),
801
+ * implement this interface using the platform's native storage APIs.
802
+ */
803
+ interface CredentialStore {
804
+ /**
805
+ * Retrieves the stored access token.
806
+ *
807
+ * @returns The access token, or null if none is stored.
808
+ */
809
+ getAccessToken(): Promise<string | null>;
810
+ /**
811
+ * Stores an access token.
812
+ *
813
+ * @param token - The access token to store.
814
+ */
815
+ setAccessToken(token: string): Promise<void>;
816
+ /**
817
+ * Removes the stored access token.
818
+ */
819
+ clearAccessToken(): Promise<void>;
820
+ }
821
+
822
+ /**
823
+ * Device authorization flow for the Tentacle SDK.
824
+ *
825
+ * Implements OAuth 2.0 Device Authorization Grant (RFC 8628) for
826
+ * authenticating devices that cannot easily display a web browser.
827
+ *
828
+ * @module auth/device-auth
829
+ */
830
+
831
+ /**
832
+ * Result of initiating device authorization.
833
+ */
834
+ interface DeviceAuthInitResult {
835
+ /** Code used by SDK to poll for authorization status. */
836
+ deviceCode: string;
837
+ /** User-friendly code displayed to the user (e.g., "ABCD-1234"). */
838
+ userCode: string;
839
+ /** URL where user should navigate to enter the code. */
840
+ verificationUrl: string;
841
+ /** Seconds until this authorization request expires. */
842
+ expiresIn: number;
843
+ /** Recommended polling interval in seconds. */
844
+ interval: number;
845
+ }
846
+ /**
847
+ * Result of polling device authorization status.
848
+ */
849
+ interface DeviceAuthPollResult {
850
+ /** Current status of the authorization request. */
851
+ status: "pending" | "approved" | "denied" | "expired";
852
+ /** Access token (only present when status is "approved"). */
853
+ accessToken?: string;
854
+ }
855
+ /**
856
+ * Options for the device authorization flow.
857
+ */
858
+ interface DeviceAuthOptions {
859
+ /** Optional client information displayed to the user during approval. */
860
+ clientInfo?: string;
861
+ /**
862
+ * Callback invoked when user authorization is required.
863
+ * Display the verification URL and code to the user.
864
+ */
865
+ onAuthRequired: (params: {
866
+ verificationUrl: string;
867
+ userCode: string;
868
+ }) => void;
869
+ /**
870
+ * Optional callback invoked when authorization is approved.
871
+ */
872
+ onAuthApproved?: () => void;
873
+ /**
874
+ * Optional callback invoked when authorization is denied.
875
+ */
876
+ onAuthDenied?: () => void;
877
+ /**
878
+ * Optional callback invoked when authorization times out.
879
+ */
880
+ onAuthTimeout?: () => void;
881
+ /**
882
+ * Timeout in milliseconds for the polling loop.
883
+ * Defaults to 600000 (10 minutes).
884
+ */
885
+ pollTimeout?: number;
886
+ }
887
+ /**
888
+ * Device authorization flow implementation.
889
+ *
890
+ * @example
891
+ * ```typescript
892
+ * const flow = new DeviceAuthFlow("https://api.tentacle.live")
893
+ *
894
+ * // Initiate the flow
895
+ * const { deviceCode, userCode, verificationUrl } = await flow.initiate({
896
+ * clientInfo: "My Game v1.0",
897
+ * })
898
+ *
899
+ * // Display to user
900
+ * console.log(`Visit ${verificationUrl}`)
901
+ * console.log(`Enter code: ${userCode}`)
902
+ *
903
+ * // Poll until approved
904
+ * const result = await flow.pollUntilComplete(deviceCode, 5000)
905
+ * if (result.status === "approved") {
906
+ * console.log(`Authenticated! Token: ${result.accessToken}`)
907
+ * }
908
+ * ```
909
+ */
910
+ declare class DeviceAuthFlow {
911
+ private readonly baseUrl;
912
+ constructor(baseUrl: string);
913
+ /**
914
+ * Initiates a device authorization request.
915
+ *
916
+ * @param options - Options for the request.
917
+ * @returns Device and user codes for authorization.
918
+ */
919
+ initiate(options?: {
920
+ clientInfo?: string;
921
+ }): Promise<DeviceAuthInitResult>;
922
+ /**
923
+ * Polls for the current authorization status.
924
+ *
925
+ * @param deviceCode - The device code from initiate().
926
+ * @returns Current authorization status.
927
+ */
928
+ poll(deviceCode: string): Promise<DeviceAuthPollResult>;
929
+ /**
930
+ * Polls until authorization is complete (approved, denied, or expired).
931
+ *
932
+ * @param deviceCode - The device code from initiate().
933
+ * @param intervalMs - Polling interval in milliseconds.
934
+ * @param timeoutMs - Maximum time to wait in milliseconds.
935
+ * @returns Final authorization result.
936
+ */
937
+ pollUntilComplete(deviceCode: string, intervalMs?: number, timeoutMs?: number): Promise<DeviceAuthPollResult>;
938
+ }
939
+
940
+ /**
941
+ * Main Tentacle SDK client.
942
+ *
943
+ * @module client
944
+ */
945
+
946
+ /**
947
+ * Configuration options for the TentacleClient.
948
+ */
949
+ interface TentacleClientConfig {
950
+ /**
951
+ * Base URL of the Tentacle API.
952
+ *
953
+ * @example "https://api.tentacle.live"
954
+ */
955
+ baseUrl: string;
956
+ /**
957
+ * Access token for authentication.
958
+ * Required for most API operations.
959
+ *
960
+ * You can obtain an access token from the Tentacle dashboard.
961
+ *
962
+ * @example "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
963
+ */
964
+ accessToken?: string;
965
+ }
966
+ /**
967
+ * Options for creating a TentacleClient with automatic authentication.
968
+ */
969
+ interface TentacleClientCreateOptions {
970
+ /**
971
+ * Base URL of the Tentacle API.
972
+ *
973
+ * @example "https://api.tentacle.live"
974
+ */
975
+ baseUrl: string;
976
+ /**
977
+ * Credential store for persisting access tokens.
978
+ * The SDK will check for a cached token and use it if valid.
979
+ */
980
+ credentialStore: CredentialStore;
981
+ /**
982
+ * Options for the device authorization flow.
983
+ * Used when no valid cached token is available.
984
+ */
985
+ deviceAuthOptions: DeviceAuthOptions;
986
+ }
987
+ /**
988
+ * Apps API for managing apps and viewer properties.
989
+ */
990
+ declare class AppsApi {
991
+ private readonly http;
992
+ constructor(http: HttpClient);
993
+ /**
994
+ * Create a new app.
995
+ *
996
+ * @param options - App creation options
997
+ * @param options.name - Name of the app
998
+ * @returns The created app
999
+ *
1000
+ * @example
1001
+ * ```typescript
1002
+ * const { app } = await client.apps.create({ name: "My Game" })
1003
+ * console.log(`Created app: ${app.id}`)
1004
+ * ```
1005
+ */
1006
+ create(options: {
1007
+ name: string;
1008
+ }): Promise<{
1009
+ app: AppJson;
1010
+ }>;
1011
+ /**
1012
+ * List all apps for the current user.
1013
+ *
1014
+ * @returns Array of apps
1015
+ *
1016
+ * @example
1017
+ * ```typescript
1018
+ * const { apps } = await client.apps.list()
1019
+ * for (const app of apps) {
1020
+ * console.log(`${app.name}: ${app.id}`)
1021
+ * }
1022
+ * ```
1023
+ */
1024
+ list(): Promise<{
1025
+ apps: AppJson[];
1026
+ }>;
1027
+ /**
1028
+ * Get an app by name.
1029
+ *
1030
+ * @param options - Query options
1031
+ * @param options.name - Name of the app
1032
+ * @returns The app
1033
+ *
1034
+ * @example
1035
+ * ```typescript
1036
+ * const { app } = await client.apps.getByName({ name: "My Game" })
1037
+ * console.log(`App ID: ${app.id}`)
1038
+ * ```
1039
+ */
1040
+ getByName(options: {
1041
+ name: string;
1042
+ }): Promise<{
1043
+ app: AppJson;
1044
+ }>;
1045
+ /**
1046
+ * List apps for a viewer.
1047
+ * Requires viewer authentication.
1048
+ *
1049
+ * @param options - Query options
1050
+ * @param options.where - Filter options
1051
+ * @param options.where.isActive - Filter by active status
1052
+ * @returns Array of apps
1053
+ *
1054
+ * @example
1055
+ * ```typescript
1056
+ * const { apps } = await client.apps.listForViewer({ where: { isActive: true } })
1057
+ * ```
1058
+ */
1059
+ listForViewer(options?: {
1060
+ where?: {
1061
+ isActive?: boolean;
1062
+ };
1063
+ }): Promise<{
1064
+ apps: AppJson[];
1065
+ }>;
1066
+ /**
1067
+ * Create or update viewer properties.
1068
+ *
1069
+ * @param properties - Array of viewer properties to create/update
1070
+ * @returns Success status
1071
+ *
1072
+ * @example
1073
+ * ```typescript
1074
+ * await client.apps.createViewerProperties([
1075
+ * { id: "prop1", appId: "app1", viewerId: "v1", name: "score", type: "number", value: 100 },
1076
+ * ])
1077
+ * ```
1078
+ */
1079
+ createViewerProperties(properties: ViewerPropertyJson[]): Promise<{
1080
+ success: boolean;
1081
+ }>;
1082
+ /**
1083
+ * Update viewer properties.
1084
+ *
1085
+ * @param properties - Array of viewer properties to update
1086
+ * @returns Success status
1087
+ *
1088
+ * @example
1089
+ * ```typescript
1090
+ * await client.apps.updateViewerProperties([
1091
+ * { id: "prop1", appId: "app1", viewerId: "v1", name: "score", type: "number", value: 150 },
1092
+ * ])
1093
+ * ```
1094
+ */
1095
+ updateViewerProperties(properties: ViewerPropertyJson[]): Promise<{
1096
+ success: boolean;
1097
+ }>;
1098
+ /**
1099
+ * Get viewers by property.
1100
+ *
1101
+ * @param options - Query options
1102
+ * @param options.appId - App ID
1103
+ * @param options.propertyName - Property name to query
1104
+ * @param options.propertyType - Property type
1105
+ * @param options.orderDirection - Order direction (asc/desc)
1106
+ * @param options.take - Number of results (1-1000, default 20)
1107
+ * @returns Viewers with the specified property
1108
+ *
1109
+ * @example
1110
+ * ```typescript
1111
+ * const result = await client.apps.getViewersByProperty({
1112
+ * appId: "app1",
1113
+ * propertyName: "score",
1114
+ * propertyType: "number",
1115
+ * orderDirection: "desc",
1116
+ * take: 10,
1117
+ * })
1118
+ * for (const viewer of result.viewers) {
1119
+ * console.log(`${viewer.twitch?.displayName}: ${viewer.properties.score.value}`)
1120
+ * }
1121
+ * ```
1122
+ */
1123
+ getViewersByProperty(options: {
1124
+ appId: string;
1125
+ propertyName: string;
1126
+ propertyType: ViewerPropertyType;
1127
+ orderDirection?: OrderDirection;
1128
+ take?: number;
1129
+ }): Promise<ViewersByPropertyOutput>;
1130
+ }
1131
+ /**
1132
+ * Stream API for accessing chat messages and events.
1133
+ */
1134
+ declare class StreamApi {
1135
+ private readonly http;
1136
+ constructor(http: HttpClient);
1137
+ /**
1138
+ * Get the latest chat messages from Twitch and Kick.
1139
+ *
1140
+ * @param options - Query options
1141
+ * @param options.take - Maximum number of messages to return (default: 50)
1142
+ * @param options.isCommand - Filter to only command messages (starting with !)
1143
+ * @returns Array of chat messages from both platforms
1144
+ *
1145
+ * @example
1146
+ * ```typescript
1147
+ * // Get latest 50 chat messages
1148
+ * const messages = await client.stream.getChatMessages()
1149
+ *
1150
+ * // Get latest 10 command messages
1151
+ * const commands = await client.stream.getChatMessages({
1152
+ * take: 10,
1153
+ * isCommand: true,
1154
+ * })
1155
+ *
1156
+ * for (const msg of messages) {
1157
+ * console.log(`[${msg.$platform}] ${msg.$text}`)
1158
+ * }
1159
+ * ```
1160
+ */
1161
+ getChatMessages(options?: {
1162
+ take?: number;
1163
+ isCommand?: boolean;
1164
+ }): Promise<StreamChatMessageJson[]>;
1165
+ /**
1166
+ * Get the latest stream events from Twitch and Kick.
1167
+ *
1168
+ * Events include follows, subscriptions, raids, cheers, and more.
1169
+ *
1170
+ * @param options - Query options
1171
+ * @param options.take - Maximum number of events to return (default: 50)
1172
+ * @returns Array of stream events from both platforms
1173
+ *
1174
+ * @example
1175
+ * ```typescript
1176
+ * const events = await client.stream.getEvents({ take: 20 })
1177
+ *
1178
+ * for (const event of events) {
1179
+ * if (event.$platform === 'twitch') {
1180
+ * switch (event.$type) {
1181
+ * case 'channel.follow':
1182
+ * console.log(`New follower: ${event.userDisplayName}`)
1183
+ * break
1184
+ * case 'channel.subscribe':
1185
+ * console.log(`New subscriber: ${event.userDisplayName}`)
1186
+ * break
1187
+ * }
1188
+ * }
1189
+ * }
1190
+ * ```
1191
+ */
1192
+ getEvents(options?: {
1193
+ take?: number;
1194
+ }): Promise<StreamEventJson[]>;
1195
+ }
1196
+ /**
1197
+ * Subathon API for accessing subathon statistics.
1198
+ */
1199
+ declare class SubathonApi {
1200
+ private readonly http;
1201
+ constructor(http: HttpClient);
1202
+ /**
1203
+ * Get subscription stats.
1204
+ *
1205
+ * @returns Subscription statistics
1206
+ *
1207
+ * @example
1208
+ * ```typescript
1209
+ * const stats = await client.subathonStats.getSubs()
1210
+ * console.log(`Subs: ${stats.count}/${stats.goal}`)
1211
+ * ```
1212
+ */
1213
+ getSubs(): Promise<SubathonStats_Subscriptions>;
1214
+ /**
1215
+ * Get gifted subscription stats.
1216
+ *
1217
+ * @returns Gifted subscription statistics
1218
+ *
1219
+ * @example
1220
+ * ```typescript
1221
+ * const stats = await client.subathonStats.getGiftedSubs()
1222
+ * console.log(`Gifted subs: ${stats.count}`)
1223
+ * ```
1224
+ */
1225
+ getGiftedSubs(): Promise<SubathonStats_GiftedSubscriptions>;
1226
+ /**
1227
+ * Get donation stats.
1228
+ *
1229
+ * @returns Donation statistics
1230
+ *
1231
+ * @example
1232
+ * ```typescript
1233
+ * const stats = await client.subathonStats.getDonations()
1234
+ * console.log(`Donations: $${stats.dollars}`)
1235
+ * ```
1236
+ */
1237
+ getDonations(): Promise<SubathonStats_Donations>;
1238
+ /**
1239
+ * Get subscription stats for overlay.
1240
+ * Public endpoint - no authentication required.
1241
+ *
1242
+ * @param options - Query options
1243
+ * @param options.email - User email
1244
+ * @returns Subscription statistics or null if user not found
1245
+ *
1246
+ * @example
1247
+ * ```typescript
1248
+ * const stats = await client.subathonStats.getOverlaySubs({ email: "user@example.com" })
1249
+ * if (stats) {
1250
+ * console.log(`Subs: ${stats.count}`)
1251
+ * }
1252
+ * ```
1253
+ */
1254
+ getOverlaySubs(options: {
1255
+ email: string;
1256
+ }): Promise<SubathonStats_Subscriptions | null>;
1257
+ /**
1258
+ * Get gifted subscription stats for overlay.
1259
+ * Public endpoint - no authentication required.
1260
+ *
1261
+ * @param options - Query options
1262
+ * @param options.email - User email
1263
+ * @returns Gifted subscription statistics or null if user not found
1264
+ *
1265
+ * @example
1266
+ * ```typescript
1267
+ * const stats = await client.subathonStats.getOverlayGiftedSubs({ email: "user@example.com" })
1268
+ * ```
1269
+ */
1270
+ getOverlayGiftedSubs(options: {
1271
+ email: string;
1272
+ }): Promise<SubathonStats_GiftedSubscriptions | null>;
1273
+ /**
1274
+ * Get donation stats for overlay.
1275
+ * Public endpoint - no authentication required.
1276
+ *
1277
+ * @param options - Query options
1278
+ * @param options.email - User email
1279
+ * @returns Donation statistics or null if user not found
1280
+ *
1281
+ * @example
1282
+ * ```typescript
1283
+ * const stats = await client.subathonStats.getOverlayDonations({ email: "user@example.com" })
1284
+ * ```
1285
+ */
1286
+ getOverlayDonations(options: {
1287
+ email: string;
1288
+ }): Promise<SubathonStats_Donations | null>;
1289
+ }
1290
+ /**
1291
+ * Viewer API for accessing viewer data.
1292
+ */
1293
+ declare class ViewerApi {
1294
+ private readonly http;
1295
+ constructor(http: HttpClient);
1296
+ /**
1297
+ * Create a viewer action.
1298
+ * Requires viewer authentication.
1299
+ *
1300
+ * @param options - Action options
1301
+ * @param options.name - Action name
1302
+ * @param options.data - Action data
1303
+ * @param options.appId - App ID
1304
+ * @returns Success status
1305
+ *
1306
+ * @example
1307
+ * ```typescript
1308
+ * await client.viewer.createAction({
1309
+ * name: "button_click",
1310
+ * data: { buttonId: "play" },
1311
+ * appId: "app1",
1312
+ * })
1313
+ * ```
1314
+ */
1315
+ createAction(options: {
1316
+ name: string;
1317
+ data: unknown;
1318
+ appId: string;
1319
+ }): Promise<{
1320
+ success: boolean;
1321
+ }>;
1322
+ /**
1323
+ * Get mini viewer data.
1324
+ *
1325
+ * @param options - Query options
1326
+ * @param options.viewerId - Viewer ID
1327
+ * @returns Viewer mini data
1328
+ *
1329
+ * @example
1330
+ * ```typescript
1331
+ * const viewer = await client.viewer.getMini({ viewerId: "v1" })
1332
+ * console.log(`Twitch: ${viewer.twitch?.displayName}`)
1333
+ * ```
1334
+ */
1335
+ getMini(options: {
1336
+ viewerId: string;
1337
+ }): Promise<ViewerMiniJson>;
1338
+ /**
1339
+ * Get full viewer data including properties.
1340
+ *
1341
+ * @param options - Query options
1342
+ * @param options.viewerId - Viewer ID
1343
+ * @returns Full viewer data
1344
+ *
1345
+ * @example
1346
+ * ```typescript
1347
+ * const viewer = await client.viewer.get({ viewerId: "v1" })
1348
+ * console.log(`Twitch: ${viewer.twitch?.displayName}`)
1349
+ * if (viewer.properties) {
1350
+ * for (const [name, prop] of Object.entries(viewer.properties)) {
1351
+ * console.log(`${name}: ${prop.value}`)
1352
+ * }
1353
+ * }
1354
+ * ```
1355
+ */
1356
+ get(options: {
1357
+ viewerId: string;
1358
+ }): Promise<ViewerJson>;
1359
+ }
1360
+ /**
1361
+ * Main client for the Tentacle API.
1362
+ *
1363
+ * Provides access to Tentacle API endpoints including:
1364
+ * - **realtime**: Subscribe to live Twitch and Kick events via SSE
1365
+ * - **apps**: Manage apps and viewer properties
1366
+ * - **stream**: Access chat messages and events history
1367
+ * - **subathon**: Access subathon statistics
1368
+ * - **viewer**: Access viewer data
1369
+ *
1370
+ * @example
1371
+ * ```typescript
1372
+ * import { TentacleClient } from 'tentacle-sdk'
1373
+ *
1374
+ * const client = new TentacleClient({
1375
+ * baseUrl: 'https://api.tentacle.live',
1376
+ * accessToken: 'your-access-token',
1377
+ * })
1378
+ *
1379
+ * // Subscribe to realtime events (Twitch + Kick)
1380
+ * const unsubscribe = client.realtime.subscribe({
1381
+ * onEvent: (event) => {
1382
+ * if (event.kind === 'StreamChatMessage') {
1383
+ * console.log(`Chat: ${event.payload.$text}`)
1384
+ * } else if (event.kind === 'StreamEvent') {
1385
+ * console.log(`Event: ${event.payload.$type}`)
1386
+ * }
1387
+ * },
1388
+ * })
1389
+ *
1390
+ * // Get recent chat messages
1391
+ * const messages = await client.stream.getChatMessages({ take: 10 })
1392
+ * ```
1393
+ */
1394
+ declare class TentacleClient {
1395
+ private readonly http;
1396
+ /**
1397
+ * Creates a TentacleClient with automatic authentication.
1398
+ *
1399
+ * This factory method handles the device authorization flow:
1400
+ * 1. Checks for a cached token in the credential store
1401
+ * 2. Validates the cached token with the API
1402
+ * 3. If invalid or missing, initiates device authorization
1403
+ * 4. Caches the new token after successful authorization
1404
+ *
1405
+ * @param options - Creation options
1406
+ * @returns A configured TentacleClient
1407
+ *
1408
+ * @example
1409
+ * ```typescript
1410
+ * const client = await TentacleClient.create({
1411
+ * baseUrl: "https://api.tentacle.live",
1412
+ * credentialStore: new FileCredentialStore(),
1413
+ * deviceAuthOptions: {
1414
+ * clientInfo: "My Unreal Game",
1415
+ * onAuthRequired: ({ verificationUrl, userCode }) => {
1416
+ * console.log(`Visit: ${verificationUrl}`)
1417
+ * console.log(`Enter code: ${userCode}`)
1418
+ * },
1419
+ * },
1420
+ * })
1421
+ * ```
1422
+ */
1423
+ static create(options: TentacleClientCreateOptions): Promise<TentacleClient>;
1424
+ /**
1425
+ * Apps API for managing apps and viewer properties.
1426
+ *
1427
+ * @example
1428
+ * ```typescript
1429
+ * const { apps } = await client.apps.list()
1430
+ * const { app } = await client.apps.create({ name: "My Game" })
1431
+ * ```
1432
+ */
1433
+ readonly apps: AppsApi;
1434
+ /**
1435
+ * Realtime API for subscribing to live Twitch and Kick events.
1436
+ *
1437
+ * Use this to receive:
1438
+ * - Chat messages from Twitch and Kick
1439
+ * - Stream events (follows, subs, raids, cheers, etc.)
1440
+ * - Viewer activity updates
1441
+ *
1442
+ * @example
1443
+ * ```typescript
1444
+ * const unsubscribe = client.realtime.subscribe({
1445
+ * onEvent: (event) => {
1446
+ * switch (event.kind) {
1447
+ * case 'StreamChatMessage':
1448
+ * console.log(event.payload.$text)
1449
+ * break
1450
+ * case 'StreamEvent':
1451
+ * console.log(event.payload.$type)
1452
+ * break
1453
+ * }
1454
+ * },
1455
+ * })
1456
+ * ```
1457
+ */
1458
+ readonly realtime: RealtimeApi;
1459
+ /**
1460
+ * Stream API for accessing chat messages and events history.
1461
+ *
1462
+ * @example
1463
+ * ```typescript
1464
+ * const messages = await client.stream.getChatMessages({ take: 50 })
1465
+ * const events = await client.stream.getEvents({ take: 20 })
1466
+ * ```
1467
+ */
1468
+ readonly stream: StreamApi;
1469
+ /**
1470
+ * Subathon API for accessing subathon statistics.
1471
+ *
1472
+ * @example
1473
+ * ```typescript
1474
+ * const subs = await client.subathon.getSubs()
1475
+ * const donations = await client.subathon.getDonations()
1476
+ * ```
1477
+ */
1478
+ readonly subathon: SubathonApi;
1479
+ /**
1480
+ * Viewer API for accessing viewer data.
1481
+ *
1482
+ * @example
1483
+ * ```typescript
1484
+ * const viewer = await client.viewer.get({ viewerId: "v1" })
1485
+ * ```
1486
+ */
1487
+ readonly viewer: ViewerApi;
1488
+ /**
1489
+ * Create a new Tentacle client.
1490
+ *
1491
+ * @param config - Client configuration
1492
+ * @param config.baseUrl - Base URL of the Tentacle API
1493
+ * @param config.accessToken - Access token for authentication
1494
+ *
1495
+ * @example
1496
+ * ```typescript
1497
+ * const client = new TentacleClient({
1498
+ * baseUrl: 'https://api.tentacle.live',
1499
+ * accessToken: 'your-access-token',
1500
+ * })
1501
+ * ```
1502
+ */
1503
+ constructor(config: TentacleClientConfig);
1504
+ }
1505
+
1506
+ /**
1507
+ * File-based credential store for Node.js environments.
1508
+ *
1509
+ * Stores access tokens in a JSON file on the local filesystem.
1510
+ * Default location: ~/.tentacle/credentials.json
1511
+ *
1512
+ * @module auth/file-credential-store
1513
+ */
1514
+
1515
+ /**
1516
+ * Options for configuring the FileCredentialStore.
1517
+ */
1518
+ interface FileCredentialStoreOptions {
1519
+ /**
1520
+ * Path to the credentials file.
1521
+ * Defaults to ~/.tentacle/credentials.json
1522
+ */
1523
+ path?: string;
1524
+ }
1525
+ /**
1526
+ * File-based credential store implementation.
1527
+ *
1528
+ * Stores access tokens in a JSON file with restricted permissions (0o600).
1529
+ * Uses dynamic imports for Node.js fs module to support non-Node environments.
1530
+ *
1531
+ * @example
1532
+ * ```typescript
1533
+ * const store = new FileCredentialStore()
1534
+ * await store.setAccessToken("my-token")
1535
+ * const token = await store.getAccessToken()
1536
+ * ```
1537
+ */
1538
+ declare class FileCredentialStore implements CredentialStore {
1539
+ private readonly path;
1540
+ constructor(options?: FileCredentialStoreOptions);
1541
+ /**
1542
+ * Gets the default credentials file path.
1543
+ */
1544
+ private getDefaultPath;
1545
+ /**
1546
+ * Retrieves the stored access token.
1547
+ */
1548
+ getAccessToken(): Promise<string | null>;
1549
+ /**
1550
+ * Stores an access token.
1551
+ *
1552
+ * Creates the directory if it doesn't exist.
1553
+ * Sets file permissions to 0o600 (user read/write only).
1554
+ */
1555
+ setAccessToken(token: string): Promise<void>;
1556
+ /**
1557
+ * Removes the stored access token.
1558
+ */
1559
+ clearAccessToken(): Promise<void>;
1560
+ }
1561
+
1562
+ export { type AppJson, type ChatMessageEmoji, type ChatMessageEmote, type ChatMessagePosition, type CommonChatMessage, type CommonEventJson, type CredentialStore, type DateIsoString, DeviceAuthFlow, type DeviceAuthInitResult, type DeviceAuthOptions, type DeviceAuthPollResult, type DonationUnlock, FileCredentialStore, type FileCredentialStoreOptions, type GiftedSubUnlock, type KickBadge, type KickChatMessageJson, type KickEventJson, type KickEventJson_ChannelFollow, type KickEventJson_ChannelSubscriptionGifts, type KickEventJson_ChannelSubscriptionNew, type KickEventJson_ChannelSubscriptionRenewal, type KickEventJson_LivestreamStatusUpdated, type OrderDirection, type RealtimeEvent, type RealtimeEvent_StreamChatMessage, type RealtimeEvent_StreamEvent, type RealtimeEvent_StreamViewerActivity, type RealtimeSubscribeOptions, type StreamChatMessageJson, type StreamEventJson, type StreamPlatform, type StreamViewerActivityJson, type SubUnlock, type SubathonStats_Donations, type SubathonStats_GiftedSubscriptions, type SubathonStats_Subscriptions, TentacleClient, type TentacleClientConfig, type TentacleClientCreateOptions, TentacleError, type TwitchChatMessageJson, type TwitchEventJson, type TwitchEventJson_Cheer, type TwitchEventJson_Follow, type TwitchEventJson_Raid, type TwitchEventJson_RedemptionAdd, type TwitchEventJson_StreamOnline, type TwitchEventJson_Subscription, type TwitchEventJson_SubscriptionGift, type TwitchEventType, type TwitchUserInfo, type UnsubscribeFunction, type ViewerActionJson, type ViewerJson, type ViewerKickJson, type ViewerMiniJson, type ViewerPropertyJson, type ViewerPropertyJsonBase, type ViewerPropertyJson_Bool, type ViewerPropertyJson_Number, type ViewerPropertyJson_String, type ViewerPropertyType, type ViewerTwitchJson, type ViewersByPropertyOutput };