@allior/wmake-streamelements-events 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/package.json +3 -10
  2. package/src/react/hooks/index.ts +0 -3
  3. package/src/react/hooks/use-event-listener.ts +0 -20
  4. package/src/react/hooks/use-on-event-received.ts +0 -108
  5. package/src/react/hooks/use-on-widget-load.ts +0 -15
  6. package/src/react/index.ts +0 -3
  7. package/src/react/types/index.ts +0 -1
  8. package/src/react/types/window-events.ts +0 -6
  9. package/src/root/aggregate.ts +0 -257
  10. package/src/root/classifier.ts +0 -271
  11. package/src/root/commands.ts +0 -39
  12. package/src/root/data/field-value.ts +0 -2
  13. package/src/root/data/index.ts +0 -2
  14. package/src/root/data/widget-load.ts +0 -5
  15. package/src/root/guards/index.ts +0 -1
  16. package/src/root/guards/on-event-received.ts +0 -58
  17. package/src/root/index.ts +0 -11
  18. package/src/root/keys.ts +0 -14
  19. package/src/root/message/index.ts +0 -1
  20. package/src/root/message/twitch/index.ts +0 -2
  21. package/src/root/message/twitch/message.ts +0 -48
  22. package/src/root/message/twitch/user-message-data.ts +0 -112
  23. package/src/root/queue/alert-queue.ts +0 -31
  24. package/src/root/queue/index.ts +0 -2
  25. package/src/root/queue/next-alert-callback.ts +0 -7
  26. package/src/root/sources/alerts.ts +0 -163
  27. package/src/root/sources/index.ts +0 -5
  28. package/src/root/sources/messages.ts +0 -1611
  29. package/src/root/sources/on-widget-load-detail.ts +0 -968
  30. package/src/root/types/index.ts +0 -2
  31. package/src/root/types/on-event-received/base.ts +0 -94
  32. package/src/root/types/on-event-received/cheer.ts +0 -10
  33. package/src/root/types/on-event-received/donation.ts +0 -29
  34. package/src/root/types/on-event-received/follower.ts +0 -9
  35. package/src/root/types/on-event-received/index.ts +0 -58
  36. package/src/root/types/on-event-received/message.ts +0 -13
  37. package/src/root/types/on-event-received/moderation.ts +0 -7
  38. package/src/root/types/on-event-received/other.ts +0 -14
  39. package/src/root/types/on-event-received/raid.ts +0 -10
  40. package/src/root/types/on-event-received/subscriber.ts +0 -77
  41. package/src/root/types/on-widget-load/base.ts +0 -35
  42. package/src/root/types/on-widget-load/index.ts +0 -3
  43. package/src/root/types/on-widget-load/recents.ts +0 -33
  44. package/src/root/types/on-widget-load/session.ts +0 -102
  45. package/src/root/window-event-map.ts +0 -9
  46. package/tsconfig.app.json +0 -31
  47. package/tsconfig.json +0 -8
  48. package/tsconfig.node.json +0 -25
  49. package/vite.config.ts +0 -36
  50. package/vite.iife.config.ts +0 -52
@@ -1,271 +0,0 @@
1
- /**
2
- * Shared classification and tier parsing for Twitch/StreamElements subscription events.
3
- * Used by aggregate normalizer.
4
- */
5
-
6
- import type { ClassifiedEvent, IncomingDetail, TierInfo } from "./types";
7
-
8
- export function parseTier(raw: string | number | null | undefined): TierInfo {
9
- if (raw == null) return { tier: 0, tierText: "" };
10
- const tierText = typeof raw === "string" ? raw : String(raw);
11
- const num = parseInt(tierText, 10);
12
- if (Number.isNaN(num)) return { tier: 0, tierText };
13
- const tier = num >= 3000 ? 3 : num >= 2000 ? 2 : num >= 1000 ? 1 : 0;
14
- return { tier, tierText };
15
- }
16
-
17
- export function getCacheKey(detail: IncomingDetail): string {
18
- const listener = detail.listener;
19
- const event = detail.event ?? {};
20
- const ag = event.activityGroup;
21
- const aid = event.activityId ?? event._id;
22
- if (ag) return String(ag);
23
- if (aid) return String(aid);
24
- if (listener === "subscriber-latest" && event.bulkGifted) {
25
- return (
26
- "sl-bulk-" +
27
- (event.sender ?? "") +
28
- "-" +
29
- (event.amount ?? "") +
30
- "-" +
31
- (event.tier ?? "") +
32
- "-" +
33
- (event._id ?? "")
34
- );
35
- }
36
- return `sl-${event._id ?? event.activityId ?? ""}`;
37
- }
38
-
39
- export function classifyEvent(detail: IncomingDetail): ClassifiedEvent | null {
40
- const listener = detail.listener;
41
- const event = detail.event ?? {};
42
- const data = (event.data ?? event) as Record<string, unknown>;
43
- const tierInfo = parseTier(
44
- (data.tier ?? event.tier) as string | number | undefined,
45
- );
46
-
47
- if (listener === "event") {
48
- if (event.type === "communityGiftPurchase") {
49
- const isAnon =
50
- (data.username &&
51
- String(data.username).toLowerCase() === "anonymous") ||
52
- data.sender === "Anonymous";
53
- return {
54
- type: isAnon ? "community-gift-anonymous" : "community-gift",
55
- tier: tierInfo.tier,
56
- tierText: tierInfo.tierText,
57
- totalAmount: parseInt(String(data.amount), 10) || 1,
58
- recipients: [],
59
- sender: isAnon
60
- ? undefined
61
- : ((data.sender ?? data.displayName ?? data.username) as string),
62
- anonymousDisplayName: isAnon
63
- ? ((data.displayName as string) ?? "Anonymous")
64
- : undefined,
65
- };
66
- }
67
- if (event.type === "subscriber") {
68
- const gifted = data.gifted === true;
69
- const sender = data.sender as string | undefined;
70
- const amount = parseInt(String(data.amount), 10) || 1;
71
- const username = (data.username ?? data.displayName) as
72
- | string
73
- | undefined;
74
- const activityGroup = event.activityGroup as string | undefined;
75
-
76
- if (sender === "Anonymous" && activityGroup) {
77
- return {
78
- type: "community-gift-anonymous",
79
- _role: "recipient",
80
- activityGroup,
81
- recipient: (username ?? data.displayName) as string,
82
- tier: tierInfo.tier,
83
- tierText: tierInfo.tierText,
84
- };
85
- }
86
- if (gifted && sender && sender !== "Anonymous") {
87
- const isCommunity =
88
- data.communityGifted === true ||
89
- (activityGroup != null && activityGroup !== "");
90
- if (!isCommunity) {
91
- const firstGift =
92
- String(data.message ?? "").indexOf("first Gift Sub") !== -1;
93
- return {
94
- type: "solo-sub-to-someone",
95
- sender,
96
- recipient: (username ?? data.displayName) as string,
97
- tier: tierInfo.tier,
98
- tierText: tierInfo.tierText,
99
- firstGiftInChannel: firstGift,
100
- };
101
- }
102
- return {
103
- type: "community-gift",
104
- _role: "recipient",
105
- activityGroup: activityGroup ?? "",
106
- recipient: (username ?? data.displayName) as string,
107
- tier: tierInfo.tier,
108
- tierText: tierInfo.tierText,
109
- };
110
- }
111
- if (!gifted && amount > 1) {
112
- return {
113
- type: "sub-renewal",
114
- name: (username ?? data.displayName) as string,
115
- months: amount,
116
- tier: tierInfo.tier,
117
- tierText: tierInfo.tierText,
118
- };
119
- }
120
- if (!gifted && amount === 1) {
121
- return {
122
- type: "self-sub",
123
- name: (username ?? data.displayName) as string,
124
- tier: tierInfo.tier,
125
- tierText: tierInfo.tierText,
126
- };
127
- }
128
- if (activityGroup) {
129
- return {
130
- type: "community-gift",
131
- _role: "recipient",
132
- activityGroup,
133
- recipient: (username ?? data.displayName) as string,
134
- tier: tierInfo.tier,
135
- tierText: tierInfo.tierText,
136
- };
137
- }
138
- return {
139
- type: "unknown",
140
- detail,
141
- };
142
- }
143
- return {
144
- type: "unknown",
145
- detail,
146
- };
147
- }
148
-
149
- if (listener === "subscriber-latest") {
150
- const bulkGifted = event.bulkGifted === true;
151
- const sender = event.sender as string | undefined;
152
- const name = event.name as string | undefined;
153
- const amount = parseInt(String(event.amount), 10) || 1;
154
- const gifted = event.gifted === true;
155
- const isCommunityGift =
156
- event.isCommunityGift === true || event.communityGifted === true;
157
-
158
- if (bulkGifted && sender === "Anonymous") {
159
- return {
160
- type: "community-gift-anonymous",
161
- tier: tierInfo.tier,
162
- tierText: tierInfo.tierText,
163
- totalAmount: amount,
164
- recipients: [],
165
- };
166
- }
167
- if (bulkGifted && sender) {
168
- return {
169
- type: "community-gift",
170
- sender,
171
- tier: tierInfo.tier,
172
- tierText: tierInfo.tierText,
173
- totalAmount: amount,
174
- recipients: [],
175
- };
176
- }
177
- if (gifted && !isCommunityGift && sender) {
178
- const firstGift =
179
- String(event.message ?? "").indexOf("first Gift Sub") !== -1;
180
- return {
181
- type: "solo-sub-to-someone",
182
- sender,
183
- recipient: name ?? "",
184
- tier: tierInfo.tier,
185
- tierText: tierInfo.tierText,
186
- firstGiftInChannel: firstGift,
187
- };
188
- }
189
- if (!gifted && amount > 1) {
190
- return {
191
- type: "sub-renewal",
192
- name: name ?? "",
193
- months: amount,
194
- tier: tierInfo.tier,
195
- tierText: tierInfo.tierText,
196
- };
197
- }
198
- if (!gifted && amount === 1 && (sender == null || name === sender)) {
199
- return {
200
- type: "self-sub",
201
- name: name ?? "",
202
- tier: tierInfo.tier,
203
- tierText: tierInfo.tierText,
204
- };
205
- }
206
- return {
207
- type: "unknown",
208
- detail,
209
- };
210
- }
211
-
212
- if (listener === "follower-latest") {
213
- return {
214
- type: "follow",
215
- name: (event.name as string) ?? "",
216
- };
217
- }
218
-
219
- if (listener === "cheer-latest") {
220
- const amount = parseInt(String(event.amount), 10) || 0;
221
- return {
222
- type: "cheer",
223
- name: (event.name as string) ?? "",
224
- amount,
225
- };
226
- }
227
-
228
- if (listener === "raid-latest") {
229
- const amount = parseInt(String(event.amount), 10) || 0;
230
- return {
231
- type: "raid",
232
- name: (event.name as string) ?? "",
233
- viewers: amount,
234
- };
235
- }
236
-
237
- if (listener === "donation") {
238
- const d = detail.event?.data as
239
- | {
240
- alert_type?: string;
241
- currency?: string;
242
- billing_system?: string;
243
- id?: number;
244
- amount_main?: number;
245
- amount?: number;
246
- username?: string;
247
- message?: string;
248
- }
249
- | undefined;
250
-
251
- const donationAmount =
252
- (d?.amount_main as number | undefined) ??
253
- (d?.amount as number | undefined) ??
254
- 0;
255
-
256
- return {
257
- type: "donation",
258
- amount: donationAmount,
259
- currency: d?.currency,
260
- username: d?.username,
261
- message: d?.message,
262
- billingSystem: d?.billing_system,
263
- id: d?.id,
264
- };
265
- }
266
-
267
- return {
268
- type: "unknown",
269
- detail,
270
- };
271
- }
@@ -1,39 +0,0 @@
1
- /**
2
- * Команды, вызывающие onEventReceived с тестовыми сообщением или алертом.
3
- * Работают только в браузере (требуется window).
4
- */
5
- import { testAlerts, testMessages } from "./sources";
6
-
7
- const EVENT_NAME = "onEventReceived";
8
-
9
- /**
10
- * Отправляет тестовое сообщение по ключу: диспатчит CustomEvent onEventReceived.
11
- */
12
- export function testMessage(key: string): void {
13
- if (typeof window === "undefined") {
14
- return;
15
- }
16
- const cleanKey = key.startsWith("testMessage_")
17
- ? key.replace("testMessage_", "")
18
- : key;
19
- const msg = testMessages[cleanKey as keyof typeof testMessages];
20
- if (msg) {
21
- window.dispatchEvent(new CustomEvent(EVENT_NAME, { detail: msg }));
22
- }
23
- }
24
-
25
- /**
26
- * Отправляет тестовый алерт по ключу: диспатчит CustomEvent onEventReceived.
27
- */
28
- export function testAlert(key: string): void {
29
- if (typeof window === "undefined") {
30
- return;
31
- }
32
- const cleanKey = key.startsWith("testAlert_")
33
- ? key.replace("testAlert_", "")
34
- : key;
35
- const alert = testAlerts[cleanKey as keyof typeof testAlerts];
36
- if (alert) {
37
- window.dispatchEvent(new CustomEvent(EVENT_NAME, { detail: alert }));
38
- }
39
- }
@@ -1,2 +0,0 @@
1
- /** Минимальный тип для fieldData в WidgetLoadDetail (без зависимости от streamelements). */
2
- export type FieldValue = string | number | boolean | undefined | null;
@@ -1,2 +0,0 @@
1
- export * from "./field-value";
2
- export * from "./widget-load";
@@ -1,5 +0,0 @@
1
- import type { FieldValue } from "./field-value.js";
2
-
3
- export interface WidgetLoadDetail {
4
- fieldData: Record<string, FieldValue>;
5
- }
@@ -1 +0,0 @@
1
- export * from "./on-event-received";
@@ -1,58 +0,0 @@
1
- import type {
2
- CheerDetail,
3
- CommunityGiftPurchaseDetail,
4
- DeleteMessageDetail,
5
- DeleteMessagesDetail,
6
- DonationDetail,
7
- FollowerDetail,
8
- MinimalEvent,
9
- RaidDetail,
10
- SubscriberDetail,
11
- SubscriberLatestDetail,
12
- TwitchMessageDetail,
13
- WidgetButtonDetail,
14
- } from "@/root/types/on-event-received";
15
-
16
- export const isDetail = (d: unknown): d is MinimalEvent =>
17
- typeof d === "object" && d !== null && "listener" in d;
18
-
19
- export function createGuard<T extends MinimalEvent>(
20
- predicate: (d: MinimalEvent) => boolean,
21
- ) {
22
- return (d: unknown): d is T => isDetail(d) && predicate(d);
23
- }
24
-
25
- export const isTwitchMessageEvent = createGuard<TwitchMessageDetail>(
26
- (d) => d.listener === "message",
27
- );
28
- export const isFollowerDetail = createGuard<FollowerDetail>(
29
- (d) => d.listener === "follower-latest",
30
- );
31
- export const isSubscriberLatestDetail = createGuard<SubscriberLatestDetail>(
32
- (d) => d.listener === "subscriber-latest",
33
- );
34
- export const isCheerDetail = createGuard<CheerDetail>(
35
- (d) => d.listener === "cheer-latest",
36
- );
37
- export const isRaidDetail = createGuard<RaidDetail>(
38
- (d) => d.listener === "raid-latest",
39
- );
40
- export const isDonationDetail = createGuard<DonationDetail>(
41
- (d) => d.listener === "donation",
42
- );
43
- export const isDeleteMessageDetail = createGuard<DeleteMessageDetail>(
44
- (d) => d.listener === "delete-message",
45
- );
46
- export const isDeleteMessagesDetail = createGuard<DeleteMessagesDetail>(
47
- (d) => d.listener === "delete-messages",
48
- );
49
- export const isCommunityGiftPurchaseDetail =
50
- createGuard<CommunityGiftPurchaseDetail>(
51
- (d) => d.listener === "event" && d.event?.type === "communityGiftPurchase",
52
- );
53
- export const isSubscriberDetail = createGuard<SubscriberDetail>(
54
- (d) => d.listener === "event" && d.event?.type === "subscriber",
55
- );
56
- export const isWidgetButtonDetail = createGuard<WidgetButtonDetail>(
57
- (d) => d.listener === "event:test" && d.event?.listener === "widget-button",
58
- );
package/src/root/index.ts DELETED
@@ -1,11 +0,0 @@
1
- export * from "./aggregate.js";
2
- export * from "./classifier.js";
3
- export * from "./types";
4
- import "./window-event-map";
5
-
6
- export * from "./commands";
7
- export * from "./data";
8
- export * from "./keys";
9
- export * from "./message";
10
- export * from "./queue";
11
- export * from "./sources/index";
package/src/root/keys.ts DELETED
@@ -1,14 +0,0 @@
1
- /**
2
- * Преобразование fieldId виджета в ключ тестовых данных.
3
- */
4
- export function getTestMessageKey(fieldId: string): string | null {
5
- if (!fieldId.startsWith("testMessage")) return null;
6
- const name = fieldId.replace("testMessage", "");
7
- return name.charAt(0).toLowerCase() + name.slice(1);
8
- }
9
-
10
- export function getTestAlertKey(fieldId: string): string | null {
11
- if (!fieldId.startsWith("testAlert")) return null;
12
- const name = fieldId.replace("testAlert", "");
13
- return name.charAt(0).toLowerCase() + name.slice(1);
14
- }
@@ -1 +0,0 @@
1
- export * from "./twitch"
@@ -1,2 +0,0 @@
1
- export * from "./message";
2
- export * from "./user-message-data";
@@ -1,48 +0,0 @@
1
- export namespace TwitchMessage {
2
- export interface Data {
3
- tags: Tags;
4
- nick: string;
5
- userId: string;
6
- displayName: string;
7
- displayColor?: string;
8
- badges: Array<Badge>;
9
- channel: string;
10
- text: string;
11
- isAction: boolean;
12
- emotes: Array<Emote>;
13
- msgId: string;
14
- time?: number;
15
- }
16
-
17
- export type Tags = Record<string, unknown> & {
18
- "badge-info"?: string;
19
- badges?: string;
20
- "first-msg"?: string;
21
- turbo?: string;
22
- subscriber?: string;
23
- mod?: string;
24
- vip?: string;
25
- "user-type"?: string;
26
- "reply-parent-msg-id"?: string;
27
- "reply-parent-msg-body"?: string;
28
- "reply-parent-display-name"?: string;
29
- "reply-parent-user-login"?: string;
30
- "reply-parent-user-id"?: string;
31
- };
32
-
33
- export interface Badge {
34
- type: string;
35
- version?: string;
36
- url?: string;
37
- description?: string;
38
- }
39
-
40
- export interface Emote {
41
- type: string;
42
- name: string;
43
- id: string;
44
- urls: Record<string, string>;
45
- start?: number;
46
- end?: number;
47
- }
48
- }
@@ -1,112 +0,0 @@
1
- import type { TwitchMessage } from "./message";
2
-
3
- export enum UserRole {
4
- BROADCASTER = "broadcaster",
5
- LEAD_MODERATOR = "lead_moderator",
6
- MODERATOR = "moderator",
7
- VIP = "vip",
8
- ARTIST = "artist",
9
- FOUNDER = "founder",
10
- SUBSCRIBER = "subscriber",
11
- TURBO = "turbo",
12
- DEFAULT = "default",
13
- }
14
-
15
- type UserMessageDataOptions = {
16
- badges?: TwitchMessage.Badge[];
17
- tags?: TwitchMessage.Tags;
18
- };
19
-
20
- export const TwitchUserMessageData = ({
21
- badges = [],
22
- tags = {},
23
- }: UserMessageDataOptions) => {
24
- const includesBadgeType = (type: string) => {
25
- return !!badges.find((b) => b.type === type);
26
- };
27
-
28
- const tagBadgesString = tags.badges ?? "";
29
- const userType = tags["user-type"] ?? "";
30
-
31
- const isSubscriber =
32
- tagBadgesString.includes("subscriber/1") ||
33
- tags.subscriber === "1" ||
34
- includesBadgeType("subscriber");
35
-
36
- const isFounder =
37
- tagBadgesString.includes("founder/1") || includesBadgeType("founder");
38
-
39
- const isBroadcaster =
40
- tagBadgesString.includes("broadcaster/1") ||
41
- includesBadgeType("broadcaster");
42
-
43
- const isFirstMessageChatter = tags["first-msg"] === "1";
44
-
45
- const isTurbo = tags.turbo === "1";
46
-
47
- const isVip =
48
- tags.vip === "1" ||
49
- tagBadgesString.includes("vip/1") ||
50
- includesBadgeType("vip");
51
-
52
- const isLeadModerator =
53
- tagBadgesString.includes("lead_moderator/1") ||
54
- includesBadgeType("lead_moderator");
55
-
56
- const isModerator =
57
- ((tags.mod === "1" ||
58
- tagBadgesString.includes("moderator/1") ||
59
- userType === "mod") &&
60
- !isLeadModerator) ||
61
- includesBadgeType("moderator");
62
-
63
- const isAnyModerator = isModerator || isLeadModerator;
64
-
65
- const isArtist =
66
- tagBadgesString.includes("artist-badge/1") ||
67
- includesBadgeType("artist-badge");
68
-
69
- const roles: UserRole[] = [];
70
- if (isBroadcaster) roles.push(UserRole.BROADCASTER);
71
- if (isLeadModerator) roles.push(UserRole.LEAD_MODERATOR);
72
- if (isModerator) roles.push(UserRole.MODERATOR);
73
- if (isVip) roles.push(UserRole.VIP);
74
- if (isArtist) roles.push(UserRole.ARTIST);
75
- if (isFounder) roles.push(UserRole.FOUNDER);
76
- if (isSubscriber) roles.push(UserRole.SUBSCRIBER);
77
- if (isTurbo) roles.push(UserRole.TURBO);
78
- roles.push(UserRole.DEFAULT);
79
-
80
- const getSubscriberMonths = (): number => {
81
- for (const key of ["badge-info", "badges"]) {
82
- const tag = tags[key] as string;
83
- if (tag) {
84
- const match = tag.match(/^subscriber\/(\d+)$/);
85
- if (match) {
86
- return parseInt(match[1], 10);
87
- }
88
- }
89
- }
90
- return 0;
91
- };
92
-
93
- const getBadgeUrlByType = (type: string) =>
94
- badges.find((b) => b.type === type)?.url;
95
-
96
- return {
97
- isSubscriber,
98
- isBroadcaster,
99
- isFirstMessageChatter,
100
- isFounder,
101
- isTurbo,
102
- isVip,
103
- isAnyModerator,
104
- isModerator,
105
- isLeadModerator,
106
- isArtist,
107
- roles,
108
- includesBadgeType,
109
- getSubscriberMonths,
110
- getBadgeUrlByType,
111
- };
112
- };
@@ -1,31 +0,0 @@
1
- import type { ClassifiedEvent } from "@/root";
2
- import type { NextAlertCallback } from ".";
3
-
4
- export class AlertQueue {
5
- private readonly queue: ClassifiedEvent[] = [];
6
- public isShowingAlert: boolean = false;
7
- public onNextAlert: NextAlertCallback;
8
-
9
- constructor(onNextAlert: NextAlertCallback) {
10
- this.onNextAlert = onNextAlert;
11
- }
12
-
13
- public push(event: ClassifiedEvent) {
14
- this.queue.push(event);
15
- }
16
-
17
- // DON'T FORGET SET "this.isShowingAlert = false" AFTER EXECUTION
18
- public processNextAlert() {
19
- if (this.queue.length === 0 || this.isShowingAlert) {
20
- return;
21
- }
22
-
23
- const alertData = this.queue.shift();
24
- if (!alertData) {
25
- return;
26
- }
27
- this.isShowingAlert = true;
28
-
29
- this.onNextAlert(this, alertData);
30
- }
31
- }
@@ -1,2 +0,0 @@
1
- export * from "./alert-queue";
2
- export * from "./next-alert-callback";
@@ -1,7 +0,0 @@
1
- import type { ClassifiedEvent } from "@/root";
2
- import type { AlertQueue } from ".";
3
-
4
- export type NextAlertCallback = (
5
- self: AlertQueue,
6
- event: ClassifiedEvent | null,
7
- ) => void;