@lumiastream/ui 0.2.8-alpha.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/LSButton.d.ts +32 -0
- package/dist/LSButton.js +80 -0
- package/dist/LSCheckbox.d.ts +21 -0
- package/dist/LSCheckbox.js +113 -0
- package/dist/LSColorPicker.d.ts +19 -0
- package/dist/LSColorPicker.js +316 -0
- package/dist/LSDatePicker.d.ts +15 -0
- package/dist/LSDatePicker.js +198 -0
- package/dist/LSFontPicker.d.ts +17 -0
- package/dist/LSFontPicker.js +179 -0
- package/dist/LSInput.d.ts +32 -0
- package/dist/LSInput.js +159 -0
- package/dist/LSMultiSelect.d.ts +16 -0
- package/dist/LSMultiSelect.js +274 -0
- package/dist/LSRadio.d.ts +17 -0
- package/dist/LSRadio.js +52 -0
- package/dist/LSSelect.d.ts +12 -0
- package/dist/LSSelect.js +140 -0
- package/dist/LSSliderInput.d.ts +39 -0
- package/dist/LSSliderInput.js +342 -0
- package/dist/LSTextField.d.ts +9 -0
- package/dist/LSTextField.js +63 -0
- package/dist/LSVariableInputField.d.ts +96 -0
- package/dist/LSVariableInputField.js +1402 -0
- package/dist/components.d.ts +22 -0
- package/dist/components.js +2317 -0
- package/dist/index.d.ts +26 -863
- package/dist/index.js +3104 -1393
- package/dist/se-import.d.ts +444 -0
- package/dist/se-import.js +8094 -0
- package/dist/utils/chatMedia.d.ts +106 -0
- package/dist/utils/chatMedia.js +611 -0
- package/dist/utils.d.ts +111 -0
- package/dist/utils.js +830 -0
- package/package.json +73 -6
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
// src/utils/chatMedia.ts
|
|
2
|
+
var URL_TOKEN_REGEX = /(?:https?:\/\/|www\.)[^\s<]+/gi;
|
|
3
|
+
var TRAILING_LINK_PUNCTUATION_REGEX = /[),.!?;:'"]+$/;
|
|
4
|
+
var MEDIA_IMAGE_REGEX = /\.(gif|png|jpe?g|webp|bmp|avif)(?:[?#].*)?$/i;
|
|
5
|
+
var MEDIA_VIDEO_REGEX = /\.(mp4|webm|mov|m4v)(?:[?#].*)?$/i;
|
|
6
|
+
var MEDIA_AUDIO_REGEX = /\.(mp3|wav|ogg|oga|aac|m4a|flac|opus)(?:[?#].*)?$/i;
|
|
7
|
+
var YOUTUBE_ID_REGEX = /^[a-zA-Z0-9_-]{6,}$/;
|
|
8
|
+
var SPOTIFY_EMBED_TYPES = /* @__PURE__ */ new Set(["track", "album", "playlist", "episode", "show", "artist"]);
|
|
9
|
+
var MEDIA_PREVIEW_USER_LEVEL_VALUES = ["streamer", "moderators", "vips", "tier3", "tier2", "subscribers", "regular", "follower", "anyone"];
|
|
10
|
+
var parseMessageLinks = (value) => {
|
|
11
|
+
if (!value) {
|
|
12
|
+
return [{ text: "" }];
|
|
13
|
+
}
|
|
14
|
+
const parts = [];
|
|
15
|
+
let cursor = 0;
|
|
16
|
+
for (const match of value.matchAll(URL_TOKEN_REGEX)) {
|
|
17
|
+
const rawToken = match[0];
|
|
18
|
+
if (!rawToken) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const start = match.index ?? 0;
|
|
22
|
+
const end = start + rawToken.length;
|
|
23
|
+
if (start > cursor) {
|
|
24
|
+
parts.push({ text: value.slice(cursor, start) });
|
|
25
|
+
}
|
|
26
|
+
const trailing = rawToken.match(TRAILING_LINK_PUNCTUATION_REGEX)?.[0] ?? "";
|
|
27
|
+
const cleaned = trailing ? rawToken.slice(0, -trailing.length) : rawToken;
|
|
28
|
+
if (cleaned) {
|
|
29
|
+
const href = cleaned.startsWith("http://") || cleaned.startsWith("https://") ? cleaned : `https://${cleaned}`;
|
|
30
|
+
parts.push({ text: cleaned, url: href });
|
|
31
|
+
} else {
|
|
32
|
+
parts.push({ text: rawToken });
|
|
33
|
+
}
|
|
34
|
+
if (trailing) {
|
|
35
|
+
parts.push({ text: trailing });
|
|
36
|
+
}
|
|
37
|
+
cursor = end;
|
|
38
|
+
}
|
|
39
|
+
if (cursor < value.length) {
|
|
40
|
+
parts.push({ text: value.slice(cursor) });
|
|
41
|
+
}
|
|
42
|
+
return parts.length ? parts : [{ text: value }];
|
|
43
|
+
};
|
|
44
|
+
var tokenizeChatMessage = (value, options) => {
|
|
45
|
+
const hyperClickableLinks = options?.hyperClickableLinks !== false;
|
|
46
|
+
const previewMediaInChat = options?.previewMediaInChat !== false;
|
|
47
|
+
if (!value) {
|
|
48
|
+
return [{ type: "text", text: "" }];
|
|
49
|
+
}
|
|
50
|
+
if (!hyperClickableLinks && !previewMediaInChat) {
|
|
51
|
+
return [{ type: "text", text: value }];
|
|
52
|
+
}
|
|
53
|
+
const parts = parseMessageLinks(value);
|
|
54
|
+
const tokens = [];
|
|
55
|
+
for (const part of parts) {
|
|
56
|
+
if (!part.url) {
|
|
57
|
+
tokens.push({ type: "text", text: part.text });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const media = previewMediaInChat ? getMediaPreviewFromUrl(part.url) : null;
|
|
61
|
+
if (media) {
|
|
62
|
+
tokens.push({
|
|
63
|
+
type: "media",
|
|
64
|
+
text: part.text,
|
|
65
|
+
url: part.url,
|
|
66
|
+
media
|
|
67
|
+
});
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (hyperClickableLinks) {
|
|
71
|
+
tokens.push({
|
|
72
|
+
type: "link",
|
|
73
|
+
text: part.text,
|
|
74
|
+
url: part.url
|
|
75
|
+
});
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
tokens.push({ type: "text", text: part.text });
|
|
79
|
+
}
|
|
80
|
+
return tokens.length ? tokens : [{ type: "text", text: value }];
|
|
81
|
+
};
|
|
82
|
+
var buildChatMessageContent = ({
|
|
83
|
+
message,
|
|
84
|
+
replaceWord,
|
|
85
|
+
filteredWordsRegex,
|
|
86
|
+
emotesRaw,
|
|
87
|
+
emotesPack,
|
|
88
|
+
origin,
|
|
89
|
+
emoteParserType,
|
|
90
|
+
isCheer,
|
|
91
|
+
storeEmotes,
|
|
92
|
+
youtubeEmotes,
|
|
93
|
+
allowParserTypes
|
|
94
|
+
}) => {
|
|
95
|
+
let normalizedMessage = message ?? "";
|
|
96
|
+
if (filteredWordsRegex && typeof replaceWord === "string") {
|
|
97
|
+
normalizedMessage = normalizedMessage.replaceAll(filteredWordsRegex, replaceWord);
|
|
98
|
+
}
|
|
99
|
+
const arrFromMsg = Array.from(normalizedMessage);
|
|
100
|
+
const parserType = emoteParserType || origin;
|
|
101
|
+
const allowParserType = !allowParserTypes || allowParserTypes.includes(parserType ?? "");
|
|
102
|
+
const emotes = [];
|
|
103
|
+
const parsePluginEmotesRaw = (raw) => {
|
|
104
|
+
const text = raw?.trim?.();
|
|
105
|
+
if (!text || text[0] !== "[" && text[0] !== "{") {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
let parsed;
|
|
109
|
+
try {
|
|
110
|
+
parsed = JSON.parse(text);
|
|
111
|
+
} catch {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
const items = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.emotes) ? parsed.emotes : [];
|
|
115
|
+
if (!items.length) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
return items.map((item) => {
|
|
119
|
+
const rawStart = Number(item?.start);
|
|
120
|
+
const rawEnd = Number(item?.end);
|
|
121
|
+
const directUrls = Array.isArray(item?.urls) ? item.urls.filter((url2) => typeof url2 === "string" && url2) : [];
|
|
122
|
+
const url = typeof item?.url === "string" ? item.url : "";
|
|
123
|
+
const urls = directUrls.length ? directUrls : url ? [url] : [];
|
|
124
|
+
if (!urls.length || !Number.isFinite(rawStart) || !Number.isFinite(rawEnd)) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
let start = Math.max(0, Math.floor(rawStart));
|
|
128
|
+
let end = Math.min(Math.floor(rawEnd) + 1, arrFromMsg.length);
|
|
129
|
+
if (start > 0 && arrFromMsg[start - 1] === ":") {
|
|
130
|
+
start -= 1;
|
|
131
|
+
}
|
|
132
|
+
if (end < arrFromMsg.length && arrFromMsg[end] === ":") {
|
|
133
|
+
end += 1;
|
|
134
|
+
}
|
|
135
|
+
if (end <= start) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
id: item?.id || `${start}-${end}`,
|
|
140
|
+
urls,
|
|
141
|
+
start,
|
|
142
|
+
end
|
|
143
|
+
};
|
|
144
|
+
}).filter(Boolean);
|
|
145
|
+
};
|
|
146
|
+
const pluginRawEmotes = emotesRaw ? parsePluginEmotesRaw(emotesRaw) : [];
|
|
147
|
+
if (pluginRawEmotes.length) {
|
|
148
|
+
emotes.push(...pluginRawEmotes);
|
|
149
|
+
}
|
|
150
|
+
if (allowParserType && emotesRaw && !pluginRawEmotes.length && parserType === "twitch") {
|
|
151
|
+
for (const emote of emotesRaw.split("/")) {
|
|
152
|
+
if (!emote) continue;
|
|
153
|
+
const [emoteId, indicies] = emote.split(":");
|
|
154
|
+
if (!emoteId || !indicies) continue;
|
|
155
|
+
for (const indexSet of indicies.split(",")) {
|
|
156
|
+
const [start, end] = indexSet.split("-");
|
|
157
|
+
if (start === void 0 || end === void 0) continue;
|
|
158
|
+
emotes.push({
|
|
159
|
+
id: emoteId,
|
|
160
|
+
urls: [
|
|
161
|
+
`https://static-cdn.jtvnw.net/emoticons/v2/${emoteId}/default/dark/1.0`,
|
|
162
|
+
`https://static-cdn.jtvnw.net/emoticons/v2/${emoteId}/default/dark/2.0`,
|
|
163
|
+
`https://static-cdn.jtvnw.net/emoticons/v2/${emoteId}/default/dark/3.0`
|
|
164
|
+
],
|
|
165
|
+
start: Math.max(+start, 0),
|
|
166
|
+
end: Math.min(+end + 1, normalizedMessage.length)
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} else if (allowParserType && emotesPack && parserType === "kick") {
|
|
171
|
+
const emotesPackRecord = emotesPack;
|
|
172
|
+
Object.keys(emotesPackRecord).forEach((emoteId) => {
|
|
173
|
+
emotesPackRecord[emoteId]?.locations?.forEach((location) => {
|
|
174
|
+
const [start, end] = location.split("-");
|
|
175
|
+
if (start === void 0 || end === void 0) return;
|
|
176
|
+
let url;
|
|
177
|
+
if (emotesPackRecord[emoteId]?.type === "emote") {
|
|
178
|
+
url = `https://files.kick.com/emotes/${emoteId}/fullsize`;
|
|
179
|
+
} else {
|
|
180
|
+
url = `https://dbxmjjzl5pc1g.cloudfront.net/1065f255-473b-4a4d-a383-1282aa1ab9d5/images/emojis/${emoteId}.png`;
|
|
181
|
+
}
|
|
182
|
+
if (url) {
|
|
183
|
+
emotes.push({
|
|
184
|
+
id: emoteId,
|
|
185
|
+
urls: [url],
|
|
186
|
+
start: Math.max(+start, 0),
|
|
187
|
+
end: Math.min(+end + 1, normalizedMessage.length)
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
} else if (allowParserType && emotesPack && parserType === "discord") {
|
|
193
|
+
const emotesPackRecord = emotesPack;
|
|
194
|
+
Object.keys(emotesPackRecord).forEach((emoteId) => {
|
|
195
|
+
emotesPackRecord[emoteId]?.locations?.forEach((location) => {
|
|
196
|
+
const [start, end] = location.split("-");
|
|
197
|
+
if (start === void 0 || end === void 0) return;
|
|
198
|
+
const url = `https://cdn.discordapp.com/emojis/${emoteId}`;
|
|
199
|
+
emotes.push({
|
|
200
|
+
id: emoteId,
|
|
201
|
+
urls: [url],
|
|
202
|
+
start: Math.max(+start, 0),
|
|
203
|
+
end: Math.min(+end + 1, normalizedMessage.length)
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
let idx = -1;
|
|
209
|
+
let nextIdx = 0;
|
|
210
|
+
const separator = origin === "youtube" ? ":" : " ";
|
|
211
|
+
do {
|
|
212
|
+
nextIdx = arrFromMsg.indexOf(separator, idx + 1);
|
|
213
|
+
if (nextIdx === -1) {
|
|
214
|
+
nextIdx = arrFromMsg.length;
|
|
215
|
+
}
|
|
216
|
+
const emote = arrFromMsg.slice(idx + 1, nextIdx).join("");
|
|
217
|
+
const start = idx + 1;
|
|
218
|
+
const end = nextIdx;
|
|
219
|
+
if (emote) {
|
|
220
|
+
if (storeEmotes?.ffz?.[emote]) {
|
|
221
|
+
emotes.push({
|
|
222
|
+
id: emote,
|
|
223
|
+
urls: [storeEmotes.ffz[emote]],
|
|
224
|
+
start,
|
|
225
|
+
end
|
|
226
|
+
});
|
|
227
|
+
} else if (storeEmotes?.bttv?.[emote]) {
|
|
228
|
+
emotes.push({
|
|
229
|
+
id: emote,
|
|
230
|
+
urls: [storeEmotes.bttv[emote]],
|
|
231
|
+
start,
|
|
232
|
+
end
|
|
233
|
+
});
|
|
234
|
+
} else if (storeEmotes?.seventv?.[emote]) {
|
|
235
|
+
emotes.push({
|
|
236
|
+
id: emote,
|
|
237
|
+
urls: [storeEmotes.seventv[emote]],
|
|
238
|
+
start,
|
|
239
|
+
end
|
|
240
|
+
});
|
|
241
|
+
} else if (youtubeEmotes?.[emote]) {
|
|
242
|
+
emotes.push({
|
|
243
|
+
id: emote,
|
|
244
|
+
urls: [youtubeEmotes[emote]],
|
|
245
|
+
start,
|
|
246
|
+
end
|
|
247
|
+
});
|
|
248
|
+
} else if (isCheer && /[a-z]+\d+/gi.exec(emote)) {
|
|
249
|
+
const cheerAmount = parseInt(emote.replace(/^\D+/g, ""));
|
|
250
|
+
let roundedAmount = 1;
|
|
251
|
+
let bitColor = "#979797";
|
|
252
|
+
if (cheerAmount >= 1e4) {
|
|
253
|
+
roundedAmount = 1e4;
|
|
254
|
+
bitColor = "#f43021";
|
|
255
|
+
} else if (cheerAmount >= 5e3) {
|
|
256
|
+
roundedAmount = 5e3;
|
|
257
|
+
bitColor = "#0099fe";
|
|
258
|
+
} else if (cheerAmount >= 1e3) {
|
|
259
|
+
roundedAmount = 1e3;
|
|
260
|
+
bitColor = "#1db2a5";
|
|
261
|
+
} else if (cheerAmount >= 100) {
|
|
262
|
+
roundedAmount = 100;
|
|
263
|
+
bitColor = "#9c3ee8";
|
|
264
|
+
}
|
|
265
|
+
emotes.push({
|
|
266
|
+
id: emote,
|
|
267
|
+
type: "cheer",
|
|
268
|
+
roundedAmount,
|
|
269
|
+
cheerAmount,
|
|
270
|
+
bitColor,
|
|
271
|
+
start,
|
|
272
|
+
end
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} while ((idx = arrFromMsg.indexOf(separator, nextIdx)) !== -1);
|
|
277
|
+
emotes.sort((x, y) => x.start - y.start);
|
|
278
|
+
let index = 0;
|
|
279
|
+
const content = [];
|
|
280
|
+
for (const emote of emotes) {
|
|
281
|
+
const slicedMsg = arrFromMsg.slice(index, emote.start).join("");
|
|
282
|
+
content.push(origin === "youtube" ? slicedMsg?.replaceAll(":", "") : slicedMsg);
|
|
283
|
+
if ("type" in emote && emote.type === "cheer") {
|
|
284
|
+
content.push({
|
|
285
|
+
id: emote.id,
|
|
286
|
+
type: "cheer",
|
|
287
|
+
roundedAmount: emote.roundedAmount,
|
|
288
|
+
cheerAmount: emote.cheerAmount,
|
|
289
|
+
bitColor: emote.bitColor,
|
|
290
|
+
key: `${emote.start}-${emote.end}`
|
|
291
|
+
});
|
|
292
|
+
} else if ("urls" in emote) {
|
|
293
|
+
content.push({
|
|
294
|
+
id: emote.id,
|
|
295
|
+
urls: emote.urls,
|
|
296
|
+
key: `${emote.start}-${emote.end}`
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
index = emote.end;
|
|
300
|
+
}
|
|
301
|
+
const subStrMsg = arrFromMsg.slice(index, arrFromMsg.length).join("");
|
|
302
|
+
content.push(origin === "youtube" ? subStrMsg?.replaceAll(":", "") : subStrMsg);
|
|
303
|
+
return content;
|
|
304
|
+
};
|
|
305
|
+
var resolveMediaPreviewSetting = (value, fallback) => {
|
|
306
|
+
return typeof value === "boolean" ? value : fallback;
|
|
307
|
+
};
|
|
308
|
+
var normalizeMediaPreviewUserLevels = (value) => {
|
|
309
|
+
if (Array.isArray(value)) {
|
|
310
|
+
const normalized = value.map((level) => String(level).trim().toLowerCase()).filter((level) => MEDIA_PREVIEW_USER_LEVEL_VALUES.includes(level));
|
|
311
|
+
return [...new Set(normalized)];
|
|
312
|
+
}
|
|
313
|
+
if (typeof value === "string") {
|
|
314
|
+
const normalized = value.split(",").map((level) => level.trim().toLowerCase()).filter((level) => MEDIA_PREVIEW_USER_LEVEL_VALUES.includes(level));
|
|
315
|
+
return [...new Set(normalized)];
|
|
316
|
+
}
|
|
317
|
+
return [];
|
|
318
|
+
};
|
|
319
|
+
var resolveMediaPreviewRoleSettings = (settings) => {
|
|
320
|
+
if (Array.isArray(settings.previewMediaUserLevels) || typeof settings.previewMediaUserLevels === "string") {
|
|
321
|
+
const selected = new Set(normalizeMediaPreviewUserLevels(settings.previewMediaUserLevels));
|
|
322
|
+
return {
|
|
323
|
+
previewMediaForViewers: selected.has("regular") || selected.has("follower") || selected.has("anyone"),
|
|
324
|
+
previewMediaForSubscribers: selected.has("subscribers"),
|
|
325
|
+
previewMediaForVips: selected.has("vips"),
|
|
326
|
+
previewMediaForModerators: selected.has("moderators"),
|
|
327
|
+
previewMediaForStreamer: selected.has("streamer"),
|
|
328
|
+
previewMediaForTier3: selected.has("tier3"),
|
|
329
|
+
previewMediaForTier2: selected.has("tier2"),
|
|
330
|
+
previewMediaForRegular: selected.has("regular"),
|
|
331
|
+
previewMediaForFollower: selected.has("follower"),
|
|
332
|
+
previewMediaForAnyone: selected.has("anyone")
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
const previewMediaForViewers = resolveMediaPreviewSetting(settings.previewMediaForViewers, true);
|
|
336
|
+
const previewMediaForSubscribers = resolveMediaPreviewSetting(settings.previewMediaForSubscribers, true);
|
|
337
|
+
return {
|
|
338
|
+
previewMediaForViewers,
|
|
339
|
+
previewMediaForSubscribers,
|
|
340
|
+
previewMediaForVips: resolveMediaPreviewSetting(settings.previewMediaForVips, true),
|
|
341
|
+
previewMediaForModerators: resolveMediaPreviewSetting(settings.previewMediaForModerators, true),
|
|
342
|
+
previewMediaForStreamer: resolveMediaPreviewSetting(settings.previewMediaForStreamer, true),
|
|
343
|
+
previewMediaForTier3: resolveMediaPreviewSetting(settings.previewMediaForTier3, previewMediaForSubscribers),
|
|
344
|
+
previewMediaForTier2: resolveMediaPreviewSetting(settings.previewMediaForTier2, previewMediaForSubscribers),
|
|
345
|
+
previewMediaForRegular: resolveMediaPreviewSetting(settings.previewMediaForRegular, previewMediaForViewers),
|
|
346
|
+
previewMediaForFollower: resolveMediaPreviewSetting(settings.previewMediaForFollower, previewMediaForViewers),
|
|
347
|
+
previewMediaForAnyone: resolveMediaPreviewSetting(settings.previewMediaForAnyone, previewMediaForViewers)
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
var getNormalizedUserLevels = (value) => {
|
|
351
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
352
|
+
return {};
|
|
353
|
+
}
|
|
354
|
+
const normalized = {};
|
|
355
|
+
for (const [key, levelValue] of Object.entries(value)) {
|
|
356
|
+
normalized[key.toLowerCase()] = Boolean(levelValue);
|
|
357
|
+
}
|
|
358
|
+
return normalized;
|
|
359
|
+
};
|
|
360
|
+
var isMediaPreviewAllowedForUser = (userLevelsRaw, roleSettings) => {
|
|
361
|
+
const levels = getNormalizedUserLevels(userLevelsRaw);
|
|
362
|
+
const isStreamer = levels.isself || levels.self || levels.streamer || levels.broadcaster || levels.owner;
|
|
363
|
+
const isModerator = levels.mod || levels.moderator;
|
|
364
|
+
const isVip = levels.vip || levels.vips;
|
|
365
|
+
const isTier3 = levels.tier3 || levels.tier_3;
|
|
366
|
+
const isTier2 = levels.tier2 || levels.tier_2;
|
|
367
|
+
const isSubscriber = levels.subscriber || levels.sub || levels.member || levels.members || levels.tier1 || levels.tier_1;
|
|
368
|
+
const isRegular = levels.regular;
|
|
369
|
+
const isFollower = levels.follower || levels.followers;
|
|
370
|
+
return isStreamer && roleSettings.previewMediaForStreamer || isModerator && roleSettings.previewMediaForModerators || isVip && roleSettings.previewMediaForVips || isTier3 && roleSettings.previewMediaForTier3 || isTier2 && roleSettings.previewMediaForTier2 || isSubscriber && roleSettings.previewMediaForSubscribers || isRegular && roleSettings.previewMediaForRegular || isFollower && roleSettings.previewMediaForFollower || roleSettings.previewMediaForAnyone;
|
|
371
|
+
};
|
|
372
|
+
var parseExternalUrl = (url) => {
|
|
373
|
+
try {
|
|
374
|
+
return new URL(url);
|
|
375
|
+
} catch {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
var decodeURIComponentSafe = (value) => {
|
|
380
|
+
try {
|
|
381
|
+
return decodeURIComponent(value);
|
|
382
|
+
} catch {
|
|
383
|
+
return value;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
var sanitizeMediaTitle = (value) => {
|
|
387
|
+
return decodeURIComponentSafe(value.replace(/\+/g, " ")).replace(/\.[^./?#]+$/, "").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
388
|
+
};
|
|
389
|
+
var tryExtractFilename = (value) => {
|
|
390
|
+
const trimmed = value.trim();
|
|
391
|
+
if (!trimmed) {
|
|
392
|
+
return "";
|
|
393
|
+
}
|
|
394
|
+
const normalizedUrl = /^https?:\/\//i.test(trimmed) ? trimmed : trimmed.startsWith("www.") ? `https://${trimmed}` : "";
|
|
395
|
+
if (normalizedUrl) {
|
|
396
|
+
const parsed = parseExternalUrl(normalizedUrl);
|
|
397
|
+
if (parsed) {
|
|
398
|
+
const segment2 = parsed.pathname.split("/").filter(Boolean).pop() ?? "";
|
|
399
|
+
const decoded = decodeURIComponentSafe(segment2).trim();
|
|
400
|
+
if (decoded) {
|
|
401
|
+
return decoded;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const withoutQueryAndHash = trimmed.split(/[?#]/)[0] ?? "";
|
|
406
|
+
const segment = withoutQueryAndHash.split("/").filter(Boolean).pop() ?? "";
|
|
407
|
+
return decodeURIComponentSafe(segment).trim();
|
|
408
|
+
};
|
|
409
|
+
var getAudioTitleFromUrl = (url) => {
|
|
410
|
+
const parsed = parseExternalUrl(url);
|
|
411
|
+
if (!parsed) {
|
|
412
|
+
return "Audio";
|
|
413
|
+
}
|
|
414
|
+
for (const queryKey of ["title", "name", "filename", "file"]) {
|
|
415
|
+
const rawValue = parsed.searchParams.get(queryKey);
|
|
416
|
+
const filename = rawValue ? tryExtractFilename(rawValue) : "";
|
|
417
|
+
if (filename) {
|
|
418
|
+
return filename;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const pathFilename = tryExtractFilename(parsed.pathname);
|
|
422
|
+
if (pathFilename) {
|
|
423
|
+
return pathFilename;
|
|
424
|
+
}
|
|
425
|
+
for (const queryKey of ["title", "name", "filename", "file"]) {
|
|
426
|
+
const rawValue = parsed.searchParams.get(queryKey);
|
|
427
|
+
const normalized = rawValue ? sanitizeMediaTitle(rawValue) : "";
|
|
428
|
+
if (normalized) {
|
|
429
|
+
return normalized;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
const pathSegment = parsed.pathname.split("/").filter(Boolean).pop() ?? "";
|
|
433
|
+
const normalizedPathTitle = sanitizeMediaTitle(pathSegment);
|
|
434
|
+
return normalizedPathTitle || "Audio";
|
|
435
|
+
};
|
|
436
|
+
var extractYoutubeEmbedInfo = (url) => {
|
|
437
|
+
const parsed = parseExternalUrl(url);
|
|
438
|
+
if (!parsed) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
const hostname = parsed.hostname.replace(/^www\./i, "").toLowerCase();
|
|
442
|
+
let videoId = "";
|
|
443
|
+
let isShort = false;
|
|
444
|
+
if (hostname === "youtu.be") {
|
|
445
|
+
videoId = parsed.pathname.split("/").filter(Boolean)[0] ?? "";
|
|
446
|
+
} else if (hostname.endsWith("youtube.com") || hostname === "youtube-nocookie.com") {
|
|
447
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
448
|
+
if (segments[0] === "watch") {
|
|
449
|
+
videoId = parsed.searchParams.get("v") ?? "";
|
|
450
|
+
} else if (segments[0] === "shorts") {
|
|
451
|
+
videoId = segments[1] ?? "";
|
|
452
|
+
isShort = true;
|
|
453
|
+
} else if (["embed", "live", "v"].includes(segments[0] ?? "")) {
|
|
454
|
+
videoId = segments[1] ?? "";
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const cleaned = videoId.trim();
|
|
458
|
+
return YOUTUBE_ID_REGEX.test(cleaned) ? { id: cleaned, isShort } : null;
|
|
459
|
+
};
|
|
460
|
+
var extractSpotifyEmbedUrl = (url) => {
|
|
461
|
+
const parsed = parseExternalUrl(url);
|
|
462
|
+
if (!parsed) {
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
const hostname = parsed.hostname.replace(/^www\./i, "").toLowerCase();
|
|
466
|
+
if (hostname !== "open.spotify.com" && hostname !== "play.spotify.com") {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
470
|
+
let type = segments[0] ?? "";
|
|
471
|
+
let id = segments[1] ?? "";
|
|
472
|
+
if (type === "intl" && segments.length >= 4) {
|
|
473
|
+
type = segments[2] ?? "";
|
|
474
|
+
id = segments[3] ?? "";
|
|
475
|
+
}
|
|
476
|
+
if (!SPOTIFY_EMBED_TYPES.has(type) || !id) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
return `https://open.spotify.com/embed/${type}/${id}`;
|
|
480
|
+
};
|
|
481
|
+
var getMediaPreviewFromUrl = (url) => {
|
|
482
|
+
if (MEDIA_IMAGE_REGEX.test(url)) {
|
|
483
|
+
return { kind: "image", src: url };
|
|
484
|
+
}
|
|
485
|
+
if (MEDIA_AUDIO_REGEX.test(url)) {
|
|
486
|
+
return { kind: "audio", src: url, title: getAudioTitleFromUrl(url) };
|
|
487
|
+
}
|
|
488
|
+
if (MEDIA_VIDEO_REGEX.test(url)) {
|
|
489
|
+
return { kind: "video", src: url };
|
|
490
|
+
}
|
|
491
|
+
const youtube = extractYoutubeEmbedInfo(url);
|
|
492
|
+
if (youtube) {
|
|
493
|
+
return { kind: youtube.isShort ? "youtubeShort" : "youtube", src: `https://www.youtube.com/embed/${youtube.id}` };
|
|
494
|
+
}
|
|
495
|
+
const spotifyEmbed = extractSpotifyEmbedUrl(url);
|
|
496
|
+
if (spotifyEmbed) {
|
|
497
|
+
return { kind: "spotify", src: spotifyEmbed };
|
|
498
|
+
}
|
|
499
|
+
return null;
|
|
500
|
+
};
|
|
501
|
+
var normalizeProfilePlatform = (value) => {
|
|
502
|
+
if (value === null || value === void 0) {
|
|
503
|
+
return "";
|
|
504
|
+
}
|
|
505
|
+
return String(value).trim().toLowerCase();
|
|
506
|
+
};
|
|
507
|
+
var normalizeProfileHandle = (value) => {
|
|
508
|
+
const text = typeof value === "string" || typeof value === "number" ? String(value).trim() : "";
|
|
509
|
+
return text.replace(/^@+/, "");
|
|
510
|
+
};
|
|
511
|
+
var resolvePlatformChatterProfileUrl = ({ platform, username, displayname, userId }) => {
|
|
512
|
+
const normalizedPlatform = normalizeProfilePlatform(platform);
|
|
513
|
+
if (!normalizedPlatform) {
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
const normalizedUsername = normalizeProfileHandle(username) || normalizeProfileHandle(displayname);
|
|
517
|
+
const normalizedUserId = typeof userId === "string" || typeof userId === "number" ? String(userId).trim() : "";
|
|
518
|
+
switch (normalizedPlatform) {
|
|
519
|
+
case "twitch":
|
|
520
|
+
return normalizedUsername ? `https://www.twitch.tv/${encodeURIComponent(normalizedUsername)}` : null;
|
|
521
|
+
case "youtube":
|
|
522
|
+
case "youtubegaming":
|
|
523
|
+
if (/^UC[\w-]+$/i.test(normalizedUserId)) {
|
|
524
|
+
return `https://www.youtube.com/channel/${encodeURIComponent(normalizedUserId)}`;
|
|
525
|
+
}
|
|
526
|
+
if (normalizedUsername) {
|
|
527
|
+
return `https://www.youtube.com/@${encodeURIComponent(normalizedUsername)}`;
|
|
528
|
+
}
|
|
529
|
+
return normalizedUserId ? `https://www.youtube.com/channel/${encodeURIComponent(normalizedUserId)}` : null;
|
|
530
|
+
case "facebook":
|
|
531
|
+
if (normalizedUserId) {
|
|
532
|
+
return `https://www.facebook.com/${encodeURIComponent(normalizedUserId)}`;
|
|
533
|
+
}
|
|
534
|
+
return normalizedUsername ? `https://www.facebook.com/${encodeURIComponent(normalizedUsername)}` : null;
|
|
535
|
+
case "tiktok":
|
|
536
|
+
return normalizedUsername ? `https://www.tiktok.com/@${encodeURIComponent(normalizedUsername)}` : null;
|
|
537
|
+
case "kick":
|
|
538
|
+
return normalizedUsername ? `https://kick.com/${encodeURIComponent(normalizedUsername)}` : null;
|
|
539
|
+
case "discord":
|
|
540
|
+
return normalizedUserId ? `https://discord.com/users/${encodeURIComponent(normalizedUserId)}` : null;
|
|
541
|
+
case "trovo":
|
|
542
|
+
return normalizedUsername ? `https://trovo.live/${encodeURIComponent(normalizedUsername)}` : null;
|
|
543
|
+
default:
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
var normalizeHttpUrl = (value) => {
|
|
548
|
+
const candidate = typeof value === "string" ? value.trim() : "";
|
|
549
|
+
if (!candidate) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
const normalized = candidate.startsWith("http://") || candidate.startsWith("https://") ? candidate : candidate.startsWith("www.") ? `https://${candidate}` : candidate;
|
|
553
|
+
const parsed = parseExternalUrl(normalized);
|
|
554
|
+
if (!parsed || !/^https?:$/i.test(parsed.protocol)) {
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
return normalized;
|
|
558
|
+
};
|
|
559
|
+
var resolveChatterProfileUrlWithResolvers = async ({
|
|
560
|
+
platform,
|
|
561
|
+
username,
|
|
562
|
+
displayname,
|
|
563
|
+
userId,
|
|
564
|
+
extraSettings,
|
|
565
|
+
resolvers
|
|
566
|
+
}) => {
|
|
567
|
+
const normalizedPlatform = normalizeProfilePlatform(platform);
|
|
568
|
+
const normalizedUsername = normalizeProfileHandle(username);
|
|
569
|
+
const normalizedDisplayName = normalizeProfileHandle(displayname);
|
|
570
|
+
const normalizedUserId = typeof userId === "string" || typeof userId === "number" ? String(userId).trim() : "";
|
|
571
|
+
const payload = {
|
|
572
|
+
username: normalizedUsername || void 0,
|
|
573
|
+
displayname: normalizedDisplayName || void 0,
|
|
574
|
+
userId: normalizedUserId || void 0,
|
|
575
|
+
platform: normalizedPlatform || void 0,
|
|
576
|
+
extraSettings
|
|
577
|
+
};
|
|
578
|
+
for (const resolver of resolvers ?? []) {
|
|
579
|
+
if (typeof resolver !== "function") {
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
try {
|
|
583
|
+
const resolved = await resolver(payload);
|
|
584
|
+
const normalizedResolvedUrl = normalizeHttpUrl(resolved);
|
|
585
|
+
if (normalizedResolvedUrl) {
|
|
586
|
+
return normalizedResolvedUrl;
|
|
587
|
+
}
|
|
588
|
+
} catch {
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return resolvePlatformChatterProfileUrl({
|
|
592
|
+
platform: normalizedPlatform || platform,
|
|
593
|
+
username: normalizedUsername || normalizedDisplayName,
|
|
594
|
+
userId: normalizedUserId || void 0
|
|
595
|
+
});
|
|
596
|
+
};
|
|
597
|
+
export {
|
|
598
|
+
MEDIA_PREVIEW_USER_LEVEL_VALUES,
|
|
599
|
+
buildChatMessageContent,
|
|
600
|
+
getMediaPreviewFromUrl,
|
|
601
|
+
getNormalizedUserLevels,
|
|
602
|
+
isMediaPreviewAllowedForUser,
|
|
603
|
+
normalizeHttpUrl,
|
|
604
|
+
normalizeMediaPreviewUserLevels,
|
|
605
|
+
parseMessageLinks,
|
|
606
|
+
resolveChatterProfileUrlWithResolvers,
|
|
607
|
+
resolveMediaPreviewRoleSettings,
|
|
608
|
+
resolveMediaPreviewSetting,
|
|
609
|
+
resolvePlatformChatterProfileUrl,
|
|
610
|
+
tokenizeChatMessage
|
|
611
|
+
};
|