kira-arts 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2983 @@
1
+ // Src/@Types/common.ts
2
+ var KiraErrorCode = /* @__PURE__ */ ((KiraErrorCode2) => {
3
+ KiraErrorCode2["Validation"] = "VALIDATION";
4
+ KiraErrorCode2["Fetch"] = "FETCH";
5
+ KiraErrorCode2["AssetLoad"] = "ASSET_LOAD";
6
+ KiraErrorCode2["Render"] = "RENDER";
7
+ KiraErrorCode2["Config"] = "CONFIG";
8
+ return KiraErrorCode2;
9
+ })(KiraErrorCode || {});
10
+
11
+ // Src/Utils/error.utils.ts
12
+ var KiraError = class extends Error {
13
+ code;
14
+ constructor(message, code = "VALIDATION" /* Validation */) {
15
+ super(message);
16
+ this.name = "KiraError";
17
+ this.code = code;
18
+ }
19
+ };
20
+
21
+ // Src/Utils/fetch.utils.ts
22
+ import { createCanvas } from "@napi-rs/canvas";
23
+
24
+ // Src/Utils/badges.utils.ts
25
+ var ICON_BASE = "https://raw.githubusercontent.com/kenndeclouv/badges-discord/main/assets";
26
+ var BOOST_BADGE = {
27
+ name: "Server Booster",
28
+ icon: `${ICON_BASE}/boosts/discordboost1.svg`
29
+ };
30
+ async function getGuildMemberInfo(client, guildId, userId) {
31
+ try {
32
+ const guild = client.guilds.cache.get(guildId) ?? await client.guilds.fetch(guildId);
33
+ const member = await guild.members.fetch(userId);
34
+ const displayColor = member.displayHexColor;
35
+ return {
36
+ boostBadge: member.premiumSince ? BOOST_BADGE : null,
37
+ roleColor: displayColor && displayColor !== "#000000" ? displayColor : null
38
+ };
39
+ } catch {
40
+ return { boostBadge: null, roleColor: null };
41
+ }
42
+ }
43
+ var FLAG_BADGES = {
44
+ Staff: { name: "Discord Staff", icon: `${ICON_BASE}/discordstaff.svg` },
45
+ Partner: { name: "Partnered Server Owner", icon: `${ICON_BASE}/discordpartner.svg` },
46
+ CertifiedModerator: { name: "Moderator Programs Alumni", icon: `${ICON_BASE}/discordmod.svg` },
47
+ Hypesquad: { name: "HypeSquad Events", icon: `${ICON_BASE}/hypesquadevents.svg` },
48
+ BugHunterLevel2: { name: "Discord Bug Hunter", icon: `${ICON_BASE}/discordbughunter2.svg` },
49
+ BugHunterLevel1: { name: "Discord Bug Hunter", icon: `${ICON_BASE}/discordbughunter1.svg` },
50
+ HypeSquadOnlineHouse1: { name: "HypeSquad Bravery", icon: `${ICON_BASE}/hypesquadbravery.svg` },
51
+ HypeSquadOnlineHouse2: {
52
+ name: "HypeSquad Brilliance",
53
+ icon: `${ICON_BASE}/hypesquadbrilliance.svg`
54
+ },
55
+ HypeSquadOnlineHouse3: { name: "HypeSquad Balance", icon: `${ICON_BASE}/hypesquadbalance.svg` },
56
+ VerifiedDeveloper: {
57
+ name: "Early Verified Bot Developer",
58
+ icon: `${ICON_BASE}/discordbotdev.svg`
59
+ },
60
+ ActiveDeveloper: { name: "Active Developer", icon: `${ICON_BASE}/activedeveloper.svg` },
61
+ VerifiedBot: { name: "Verified Bot", icon: `${ICON_BASE}/verifiedbot.svg` },
62
+ PremiumEarlySupporter: {
63
+ name: "Early Supporter",
64
+ icon: `${ICON_BASE}/discordearlysupporter.svg`
65
+ }
66
+ };
67
+ var DISPLAY_ORDER = [
68
+ "Staff",
69
+ "Partner",
70
+ "CertifiedModerator",
71
+ "Hypesquad",
72
+ "BugHunterLevel2",
73
+ "BugHunterLevel1",
74
+ "HypeSquadOnlineHouse1",
75
+ "HypeSquadOnlineHouse2",
76
+ "HypeSquadOnlineHouse3",
77
+ "VerifiedDeveloper",
78
+ "ActiveDeveloper",
79
+ "VerifiedBot",
80
+ "PremiumEarlySupporter"
81
+ ];
82
+ function getBadgesFromFlags(flags) {
83
+ if (!flags) return [];
84
+ const active = new Set(flags.toArray());
85
+ return DISPLAY_ORDER.filter((flag) => active.has(flag)).map((flag) => FLAG_BADGES[flag]).filter((badge) => Boolean(badge));
86
+ }
87
+
88
+ // Src/client.ts
89
+ var registeredClient = null;
90
+ function setClient(client) {
91
+ if (!client || typeof client.users?.fetch !== "function") {
92
+ throw new KiraError(
93
+ "setClient() expects a valid discord.js Client instance",
94
+ "CONFIG" /* Config */
95
+ );
96
+ }
97
+ registeredClient = client;
98
+ }
99
+ function getClient() {
100
+ if (!registeredClient) {
101
+ throw new KiraError(
102
+ "No discord.js Client registered. Call setClient(client) once at startup before using kira-arts, e.g. setClient(client) right after client.login().",
103
+ "CONFIG" /* Config */
104
+ );
105
+ }
106
+ return registeredClient;
107
+ }
108
+
109
+ // Src/Utils/cache.utils.ts
110
+ var DEFAULT_TTL = 12 * 60 * 60 * 1e3;
111
+ var cacheEnabled = true;
112
+ var cacheTtl = DEFAULT_TTL;
113
+ var store = /* @__PURE__ */ new Map();
114
+ function setCacheOptions(options) {
115
+ if (typeof options.enabled === "boolean") cacheEnabled = options.enabled;
116
+ if (typeof options.ttl === "number" && options.ttl > 0) cacheTtl = options.ttl;
117
+ }
118
+ function buildCacheKey(userId, guildId) {
119
+ return guildId ? `${userId}:${guildId}` : userId;
120
+ }
121
+ function getCached(key) {
122
+ if (!cacheEnabled) return null;
123
+ const entry = store.get(key);
124
+ if (!entry) return null;
125
+ if (Date.now() > entry.expiresAt) {
126
+ store.delete(key);
127
+ return null;
128
+ }
129
+ return entry.data;
130
+ }
131
+ function setCached(key, data) {
132
+ if (!cacheEnabled) return;
133
+ store.set(key, { data, expiresAt: Date.now() + cacheTtl });
134
+ }
135
+ function clearCache(userId, guildId) {
136
+ if (userId) {
137
+ store.delete(buildCacheKey(userId, guildId));
138
+ return;
139
+ }
140
+ store.clear();
141
+ }
142
+ function getCacheSize() {
143
+ return store.size;
144
+ }
145
+
146
+ // Src/Utils/validations.utils.ts
147
+ function parseImg(imgString) {
148
+ if (!imgString || typeof imgString !== "string") {
149
+ throw new KiraError(
150
+ `Invalid custom badge ('${imgString || void 0}') must be an image file 'png | jpg | jpeg | gif'`
151
+ );
152
+ }
153
+ const URL = imgString.split(".");
154
+ const imgType = URL[URL.length - 1];
155
+ const imgCheck = /(jpg|jpeg|png|gif)/gi.test(imgType);
156
+ if (!imgCheck)
157
+ throw new KiraError(
158
+ `Invalid customBackground ('${imgString || void 0}') must be an image file 'png | jpg | jpeg | gif'`
159
+ );
160
+ return imgString;
161
+ }
162
+ function parsePng(imgString) {
163
+ if (!imgString || typeof imgString !== "string") {
164
+ throw new KiraError(`Invalid custom badge ('${imgString || void 0}') must be a png file`);
165
+ }
166
+ const URL = imgString.split(".");
167
+ const imgType = URL[URL.length - 1];
168
+ const imgCheck = /(png)/gi.test(imgType);
169
+ if (!imgCheck)
170
+ throw new KiraError(`Invalid custom badge ('${imgString || void 0}') must be a png file`);
171
+ return imgString;
172
+ }
173
+ function decimalToHex(decimal) {
174
+ if (!Number.isFinite(decimal) || decimal < 0 || decimal > 16777215) {
175
+ throw new KiraError(`Invalid decimal color code (${decimal}), must be between 0 and 16777215`);
176
+ }
177
+ return "#" + Math.round(decimal).toString(16).padStart(6, "0");
178
+ }
179
+ function parseHex(input) {
180
+ const hexRegex = new RegExp(/^(#)?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/);
181
+ if (typeof input === "number") return decimalToHex(input);
182
+ let hexString = String(input ?? "").trim();
183
+ if (hexRegex.test(hexString)) {
184
+ if (!hexString.startsWith("#")) hexString = "#" + hexString;
185
+ return hexString;
186
+ }
187
+ if (/^\d+$/.test(hexString)) return decimalToHex(Number(hexString));
188
+ throw new KiraError(`Invalid Hex Code ('${input === "" ? void 0 : input}')`);
189
+ }
190
+ function isString(param, type) {
191
+ if (typeof param !== "string")
192
+ throw new KiraError(`Invalid ${type} (${param}), must be a string`);
193
+ return param;
194
+ }
195
+ function isNumber(param, type) {
196
+ if (typeof param !== "number" || isNaN(param))
197
+ throw new KiraError(`Invalid ${type} (${param}), must be a number`);
198
+ return param;
199
+ }
200
+
201
+ // Src/Utils/canvasShared.utils.ts
202
+ import { loadImage } from "@napi-rs/canvas";
203
+ function resolveCardColors(sources) {
204
+ if (sources.removeBorder) return [];
205
+ if (typeof sources.custom !== "undefined") {
206
+ const list = Array.isArray(sources.custom) ? sources.custom : [sources.custom];
207
+ return list.map((color) => parseHex(color));
208
+ }
209
+ if (sources.useNitroTheme && sources.nitroColors && sources.nitroColors.length > 0) {
210
+ return sources.nitroColors.map((color) => parseHex(color));
211
+ }
212
+ if (sources.useRoleColor && sources.roleColor) {
213
+ return [parseHex(sources.roleColor)];
214
+ }
215
+ return (sources.fallback ?? []).map(
216
+ (color) => typeof color === "string" ? color : parseHex(color)
217
+ );
218
+ }
219
+ async function loadImageSafe(url, attempts = 3, timeoutMs = 8e3) {
220
+ if (!url) return void 0;
221
+ const isRemote = /^https?:\/\//i.test(url);
222
+ if (!isRemote) {
223
+ return loadImage(url).catch((error) => {
224
+ const message = error instanceof Error ? error.message : String(error);
225
+ console.warn("[arts] failed to load local image:", url, message);
226
+ return void 0;
227
+ });
228
+ }
229
+ for (let attempt = 1; attempt <= attempts; attempt++) {
230
+ const controller = new AbortController();
231
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
232
+ try {
233
+ const response = await fetch(url, {
234
+ signal: controller.signal,
235
+ headers: { "User-Agent": "Mozilla/5.0 (compatible; kira-arts/1.0)" }
236
+ });
237
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
238
+ const arrayBuffer = await response.arrayBuffer();
239
+ return await loadImage(Buffer.from(arrayBuffer));
240
+ } catch (error) {
241
+ const message = error instanceof Error ? error.message : String(error);
242
+ if (attempt === attempts) {
243
+ console.warn("[arts] failed to load image after retries:", url, message);
244
+ return void 0;
245
+ }
246
+ await new Promise((resolve) => setTimeout(resolve, 250 * attempt));
247
+ } finally {
248
+ clearTimeout(timer);
249
+ }
250
+ }
251
+ return void 0;
252
+ }
253
+ function hexToRgb(hex) {
254
+ const normalized = parseHex(hex).replace("#", "");
255
+ const full = normalized.length === 3 ? normalized.split("").map((c) => c + c).join("") : normalized;
256
+ return {
257
+ r: parseInt(full.slice(0, 2), 16),
258
+ g: parseInt(full.slice(2, 4), 16),
259
+ b: parseInt(full.slice(4, 6), 16)
260
+ };
261
+ }
262
+ function hexToRgba(hex, alpha) {
263
+ const { r, g, b } = hexToRgb(hex);
264
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
265
+ }
266
+ function drawGradientBorder(ctx, width, height, colors, cornerRadius, thickness = 5) {
267
+ const grd = ctx.createLinearGradient(0, 0, width, height);
268
+ for (let i = 0; i < colors.length; i++) {
269
+ const stop = colors.length === 1 ? 0 : i / (colors.length - 1);
270
+ grd.addColorStop(stop, parseHex(colors[i]));
271
+ }
272
+ ctx.save();
273
+ ctx.lineWidth = thickness;
274
+ ctx.strokeStyle = grd;
275
+ ctx.beginPath();
276
+ ctx.roundRect(thickness / 2, thickness / 2, width - thickness, height - thickness, [
277
+ Math.max(0, cornerRadius - thickness / 2)
278
+ ]);
279
+ ctx.stroke();
280
+ ctx.restore();
281
+ }
282
+ function drawCoverImage(ctx, image, x, y, w, h) {
283
+ const scale = Math.max(w / image.width, h / image.height);
284
+ const drawW = image.width * scale;
285
+ const drawH = image.height * scale;
286
+ const dx = x + (w - drawW) / 2;
287
+ const dy = y + (h - drawH) / 2;
288
+ ctx.save();
289
+ ctx.beginPath();
290
+ ctx.rect(x, y, w, h);
291
+ ctx.clip();
292
+ ctx.drawImage(image, dx, dy, drawW, drawH);
293
+ ctx.restore();
294
+ }
295
+
296
+ // Src/Utils/fetch.utils.ts
297
+ var NAMEPLATE_CDN_BASE = "https://cdn.discordapp.com/assets/collectibles";
298
+ var MATH_SCRIPT_FALLBACK_MAP = {
299
+ "\u{1D49C}": "A",
300
+ \u212C: "B",
301
+ "\u{1D49E}": "C",
302
+ "\u{1D49F}": "D",
303
+ \u2130: "E",
304
+ \u2131: "F",
305
+ "\u{1D4A2}": "G",
306
+ \u210B: "H",
307
+ \u2110: "I",
308
+ "\u{1D4A5}": "J",
309
+ "\u{1D4A6}": "K",
310
+ \u2112: "L",
311
+ \u2133: "M",
312
+ "\u{1D4A9}": "N",
313
+ "\u{1D4AA}": "O",
314
+ "\u{1D4AB}": "P",
315
+ "\u{1D4AC}": "Q",
316
+ \u211B: "R",
317
+ "\u{1D4AE}": "S",
318
+ "\u{1D4AF}": "T",
319
+ "\u{1D4B0}": "U",
320
+ "\u{1D4B1}": "V",
321
+ "\u{1D4B2}": "W",
322
+ "\u{1D4B3}": "X",
323
+ "\u{1D4B4}": "Y",
324
+ "\u{1D4B5}": "Z",
325
+ "\u{1D4B6}": "a",
326
+ "\u{1D4B7}": "b",
327
+ "\u{1D4B8}": "c",
328
+ "\u{1D4B9}": "d",
329
+ \u212F: "e",
330
+ "\u{1D4BB}": "f",
331
+ \u210A: "g",
332
+ "\u{1D4BD}": "h",
333
+ "\u{1D4BE}": "i",
334
+ "\u{1D4BF}": "j",
335
+ "\u{1D4C0}": "k",
336
+ "\u{1D4C1}": "l",
337
+ "\u{1D4C2}": "m",
338
+ "\u{1D4C3}": "n",
339
+ "\u{1D45C}": "o",
340
+ "\u{1D4C5}": "p",
341
+ "\u{1D4C6}": "q",
342
+ "\u{1D4C7}": "r",
343
+ "\u{1D4C8}": "s",
344
+ "\u{1D4C9}": "t",
345
+ "\u{1D4CA}": "u",
346
+ "\u{1D4CB}": "v",
347
+ "\u{1D4CC}": "w",
348
+ "\u{1D4CD}": "x",
349
+ "\u{1D4CE}": "y",
350
+ "\u{1D4CF}": "z"
351
+ };
352
+ function applyMathScriptFallback(value) {
353
+ let result = "";
354
+ for (const char of value) {
355
+ result += MATH_SCRIPT_FALLBACK_MAP[char] ?? char;
356
+ }
357
+ return result;
358
+ }
359
+ function normalizeDisplayText(value) {
360
+ if (typeof value !== "string") return value;
361
+ try {
362
+ const normalized = value.normalize("NFKC");
363
+ if (normalized !== value) return normalized;
364
+ return applyMathScriptFallback(normalized);
365
+ } catch {
366
+ return value;
367
+ }
368
+ }
369
+ async function computeBannerAverageColor(bannerUrl) {
370
+ if (!bannerUrl) return null;
371
+ const image = await loadImageSafe(bannerUrl, 1, 4e3);
372
+ if (!image) return null;
373
+ try {
374
+ const canvas = createCanvas(1, 1);
375
+ const ctx = canvas.getContext("2d");
376
+ ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, 1, 1);
377
+ const { data } = ctx.getImageData(0, 0, 1, 1);
378
+ return "#" + [data[0], data[1], data[2]].map((channel) => channel.toString(16).padStart(2, "0")).join("");
379
+ } catch {
380
+ return null;
381
+ }
382
+ }
383
+ async function fetchNitroThemeColors(client, userId, fallbackAccent) {
384
+ try {
385
+ const profile = await client.rest.get(
386
+ `/users/${userId}/profile?with_mutual_guilds=false&with_mutual_friends=false&with_mutual_friends_count=false`
387
+ );
388
+ const themeColors = profile.user_profile_customization?.theme_colors ?? profile.theme_colors;
389
+ if (Array.isArray(themeColors) && themeColors.length >= 2) {
390
+ return [decimalToHex(themeColors[0]), decimalToHex(themeColors[1])];
391
+ }
392
+ if (Array.isArray(themeColors) && themeColors.length === 1) {
393
+ return [decimalToHex(themeColors[0])];
394
+ }
395
+ } catch (error) {
396
+ if (process.env.KIRA_DEBUG) {
397
+ const details = typeof error === "object" && error !== null && "rawError" in error ? error.rawError : error instanceof Error ? error.message : error;
398
+ console.warn(`[kira-arts] Nitro theme fetch failed for ${userId}:`, details);
399
+ }
400
+ }
401
+ return fallbackAccent ? [fallbackAccent] : null;
402
+ }
403
+ async function fetchUserData(userId, guildId, bypassCache = false) {
404
+ const cacheKey = buildCacheKey(userId, guildId);
405
+ if (!bypassCache) {
406
+ const cached = getCached(cacheKey);
407
+ if (cached) return cached;
408
+ }
409
+ const client = getClient();
410
+ try {
411
+ const user = await client.users.fetch(userId, { force: true });
412
+ const nameplateData = user.collectibles?.nameplate ?? null;
413
+ const primaryGuild = user.primaryGuild;
414
+ const hasServerTag = Boolean(primaryGuild?.identityEnabled && primaryGuild?.tag);
415
+ const badges = getBadgesFromFlags(user.flags);
416
+ let roleColor = null;
417
+ if (guildId) {
418
+ const memberInfo = await getGuildMemberInfo(client, guildId, userId);
419
+ if (memberInfo.boostBadge) badges.push(memberInfo.boostBadge);
420
+ roleColor = memberInfo.roleColor;
421
+ }
422
+ const bannerUrl = user.bannerURL({ size: 128 }) ?? null;
423
+ const accentFallback = user.hexAccentColor ?? await computeBannerAverageColor(bannerUrl);
424
+ const profileColors = await fetchNitroThemeColors(client, userId, accentFallback);
425
+ const result = {
426
+ basicInfo: {
427
+ id: user.id,
428
+ username: normalizeDisplayText(user.username),
429
+ globalName: normalizeDisplayText(user.globalName),
430
+ discriminator: user.discriminator === "0" ? null : user.discriminator,
431
+ bot: user.bot,
432
+ verified: user.flags?.has("VerifiedBot") ?? false,
433
+ createdTimestamp: user.createdTimestamp
434
+ },
435
+ assets: {
436
+ avatarURL: user.avatarURL({ size: 512, extension: "png", forceStatic: true }),
437
+ defaultAvatarURL: user.defaultAvatarURL,
438
+ bannerURL: user.bannerURL({ size: 512 }) ?? null,
439
+ badges
440
+ },
441
+ decoration: {
442
+ avatarFrame: user.avatarDecorationURL({ size: 256 }) ?? null,
443
+ profileColors,
444
+ roleColor,
445
+ nameplate: nameplateData ? {
446
+ imageURL: `${NAMEPLATE_CDN_BASE}/${nameplateData.asset}static.png`,
447
+ palette: nameplateData.palette
448
+ } : null,
449
+ serverTag: hasServerTag ? {
450
+ text: normalizeDisplayText(primaryGuild.tag),
451
+ badgeURL: user.guildTagBadgeURL({ size: 64 })
452
+ } : null
453
+ }
454
+ };
455
+ setCached(cacheKey, result);
456
+ return result;
457
+ } catch (error) {
458
+ if (error instanceof KiraError) throw error;
459
+ const message = error instanceof Error ? error.message : void 0;
460
+ throw new KiraError(message || "Could not fetch user data", "FETCH" /* Fetch */);
461
+ }
462
+ }
463
+
464
+ // Src/Utils/profileImage.output.utils.ts
465
+ import { createCanvas as createCanvas7 } from "@napi-rs/canvas";
466
+
467
+ // Src/Utils/ProfileImage/badges.ts
468
+ import { createCanvas as createCanvas2, loadImage as loadImage2 } from "@napi-rs/canvas";
469
+
470
+ // Src/Utils/fonts.utils.ts
471
+ import { GlobalFonts } from "@napi-rs/canvas";
472
+ import path from "path";
473
+ var initialized = false;
474
+ function ensureFontsRegistered() {
475
+ if (initialized) return;
476
+ initialized = true;
477
+ const fontsDir = path.join(__dirname, "..", "..", "Public", "fonts");
478
+ GlobalFonts.registerFromPath(`${fontsDir}/HelveticaBold.ttf`, "Helvetica Bold");
479
+ GlobalFonts.registerFromPath(`${fontsDir}/Helvetica.ttf`, "Helvetica");
480
+ const fallbackFonts = [
481
+ ["MPLUSRounded1c-Bold.ttf", "MPLUS Rounded"],
482
+ ["NotoSans-Bold.ttf", "Noto Sans"],
483
+ ["NotoSansSymbols-Regular.ttf", "Noto Sans Symbols"],
484
+ ["NotoSansSC-Regular.ttf", "Noto Sans SC"],
485
+ ["NotoSansJP-Regular.ttf", "Noto Sans JP"],
486
+ ["NotoColorEmoji-Regular.ttf", "Noto Color Emoji"],
487
+ ["NotoSansSymbols2-Regular.ttf", "Noto Sans Symbols 2"]
488
+ ];
489
+ for (const [file, family] of fallbackFonts) {
490
+ try {
491
+ GlobalFonts.registerFromPath(`${fontsDir}/${file}`, family);
492
+ } catch {
493
+ }
494
+ }
495
+ GlobalFonts.loadSystemFonts();
496
+ }
497
+ var FALLBACK_FAMILY_STACK = '"Noto Sans JP", "Noto Sans SC", "Noto Sans", "Noto Sans Symbols", "Noto Sans Symbols 2", "MPLUS Rounded", "Noto Color Emoji"';
498
+ function withFallback(fontDeclaration) {
499
+ return `${fontDeclaration}, ${FALLBACK_FAMILY_STACK}`;
500
+ }
501
+
502
+ // Src/Utils/strings.utils.ts
503
+ function parseUsername(username, ctx, font, size, maxLength) {
504
+ username = username && username.replace(/\s/g, "") ? username : "?????";
505
+ const usernameChars = username.split("");
506
+ let finalUsername = "";
507
+ let newSize = +size;
508
+ let textLength = 0;
509
+ let finalized = false;
510
+ while (!finalized) {
511
+ const currentUsername = usernameChars.join("");
512
+ ctx.font = withFallback(`${newSize}px ${font}`);
513
+ ctx.textAlign = "left";
514
+ ctx.fillStyle = "#FFFFFF";
515
+ const actualLength = ctx.measureText(currentUsername).width;
516
+ if (actualLength >= maxLength) {
517
+ if (newSize > 60) newSize -= 1;
518
+ else usernameChars.pop();
519
+ }
520
+ if (actualLength <= maxLength) {
521
+ finalUsername = currentUsername;
522
+ textLength = actualLength;
523
+ finalized = true;
524
+ }
525
+ }
526
+ return {
527
+ username: finalUsername,
528
+ newSize,
529
+ textLength
530
+ };
531
+ }
532
+ function getFirstDigitsAsDecimal(numString) {
533
+ const digits = (numString.length - 1) % 3 + 1;
534
+ if (numString.length < 4) {
535
+ return numString;
536
+ }
537
+ const decimal = numString.slice(digits, digits + 1);
538
+ return `${numString.slice(0, digits)}${decimal == "0" || decimal == "00" || digits == 3 ? "" : `.${decimal.replace(/0$/g, "")}`}`;
539
+ }
540
+ function abbreviateNumber(number) {
541
+ const numString = `${number}`;
542
+ const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
543
+ const abbreviations = ["", "K", "M", "B", "T"].concat(
544
+ new Array(letters.length).fill("AA").map((_, i) => letters[i].repeat(2))
545
+ );
546
+ const selectedAbbr = abbreviations[Math.floor((numString.length - 1) / 3)] ?? "??";
547
+ return `${getFirstDigitsAsDecimal(numString)}${selectedAbbr}`;
548
+ }
549
+ function getDateOrString(dateInput, createdTimestamp, localDateType = "en") {
550
+ const dateOptions = {
551
+ month: "short",
552
+ day: "numeric",
553
+ year: "numeric"
554
+ };
555
+ const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/;
556
+ if (typeof dateInput === "string") {
557
+ if (iso8601Regex.test(dateInput)) {
558
+ const dateInstance = new Date(dateInput);
559
+ return dateInstance.toLocaleDateString(localDateType, dateOptions);
560
+ } else {
561
+ return dateInput;
562
+ }
563
+ } else if (dateInput instanceof Date) {
564
+ return dateInput.toLocaleDateString(localDateType, dateOptions);
565
+ } else {
566
+ return (/* @__PURE__ */ new Date(+createdTimestamp)).toLocaleDateString(localDateType, dateOptions);
567
+ }
568
+ }
569
+ function truncateText(text, limit = 25, fromEnd = false) {
570
+ if (text.length > limit) {
571
+ if (fromEnd) {
572
+ return "..." + text.slice(-limit);
573
+ } else {
574
+ return text.slice(0, limit) + "...";
575
+ }
576
+ } else {
577
+ return text;
578
+ }
579
+ }
580
+
581
+ // Public/ProfileImage.files.json
582
+ var ProfileImage_files_default = {
583
+ otherImgs: {
584
+ botAI: "iVBORw0KGgoAAAANSUhEUgAAAEwAAAAkCAYAAAAjMNwcAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAydpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDkuMS1jMDAxIDc5LjE0NjI4OTk3NzcsIDIwMjMvMDYvMjUtMjM6NTc6MTQgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo5NjkwMEI1RjZBMjgxMUVFOTgwREFERDFFQTZENzRCMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo5NjkwMEI2MDZBMjgxMUVFOTgwREFERDFFQTZENzRCMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjk2OTAwQjVENkEyODExRUU5ODBEQUREMUVBNkQ3NEIzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjk2OTAwQjVFNkEyODExRUU5ODBEQUREMUVBNkQ3NEIzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+tQOfMQAABBxJREFUeNrsmntIU1Ecx7+bpvmYTZfmylR0aq3oLT2tqMwekLEgqeiJ1R+9IQvqj/qjIKmIKP9IjCIyqEBb9M76o+xBYpFEr7HUskzLWs1yarbOuW7Xe7drrnZ1s+5XfnDOuQ8Pn3t+j3N3ZSjdDo56E1tHLJ1YArFA/J/6TsxATE/sELGP9gMyDjAK6TgxJSRxZSK2zAYPcttgBrECCZaglDY2c+zAVMSOcOBJchZlc4Kyoo21xHpJTDoUZbRWbl9qklxSOgUWL3FwWQPkkjv+kQK6JNBvjJz8zxDz7ex/sD5yEg70nQadQoOJrwuBxjpR7luSsIpt36wvx9bq6789Py9qDoYGRLL9ZEOuFwILScTBvqlMM0URiybtesx4rcfNuodu3XZR2EiMUkS3BZbAPh0Co7C417hTX3SOfHrCEreQN9RD5oM0zlP+Wy1XDuL1g338kRU+ocsKsk7Rfc0y+Mv5C7jQ9Axbqy65GUSCMFWZ6DScpojrvsB2R83E6KAo3lhV0xfoKs64fe/s8HGC4wxEArP7AQuKwbaI8U7D/Y0ngZ/Nbt9+bq+BbPto7QPUtzR2CNN7gcn9YE1a6TS88u0VoKFahIcRi8TACLab+bkM10wvBGF2C2APElY4jRXXVyKvpliU++eFDmHbzMr6VoECs5EdY2ASqF4B7CoJ4gbtJsAvVPD44eh0JDvErTdNJqQYjok22VnKJLZtX1n5n0p5bsmF6jFgU1QjMD1EA01PFSzaDUx9xUvrJM2v6Z3sdF10+VnA+kOUidLaS+3XtovjriyuW2aohnoYWEgSbsTo2C4tFayaJdjZL611QOYLc9wCp8syqy4Tl6kUbaI6RTzPHenKEoJHazIK12NboyFkAkLa0ScFYwPUiPQNdjpW9NVIMtgdEWcZBJ2qzdXMLRZkq1N/C5cLtEuBlZFMJGuowV0So8YG87cW1E0d1WxtQSotIURUVuhwXp+65hb1pPaBUbhvLwI/vnko6FtqMO5lLnLrOn5qfobjJG41izrJ+Uqt25A98rZidWUhTporcCt2nuDxfbX3SIApF3eG/hG8TXNBXRn2fH4ieKo+ejabGDJJotr7odizwKhuf3pEXLQaj2LmYViguu0Vi/kVsqouij7B7DB+1qOwSr4+FTz3nnkwG+uYmozARmOtFxSuDe8x/HkO9n+4z3St5G/qq1Od4gKLVcN42bE9WI7ZkqnJwsd4V6W/+c0FbHpXBLkxHyCZS2wlh2h5tRe33hKSYxHLLXTFEv3l2wpJnn29868D+yJhcD16U2BGiYPLek6BnZc4uCw9BZYjuaVLooxyKDD6sdhqppyS1J4om6WUlT1LnqZ7VrR+PCaJL5ONjd6xrDiH1s80dxF7TLfc/zEki43BbhuTc/YDvwQYAOqbIjln7aR5AAAAAElFTkSuQmCC",
585
+ botNoVerif: "iVBORw0KGgoAAAANSUhEUgAAAD0AAAAkCAYAAAA6uzK6AAAACXBIWXMAAAsTAAALEwEAmpwYAAADtklEQVRogd2az2/TZhjHP04c2oBSCmgH1DGmDsiKRVuVlYaO0MLCD6FJEwjEjxtIoO3EYZO4ILF/YMft0Gm3jTIhMQESQgMhoGgLVFSUqkBRqRhQ9QCUX+JXEzc7OPZrp3bipIkNfKQcHL9f6/m+ft73eRJbymQy7Nz3Ahu+ArYDq4CPgYjdoHeMF8AD4BJwFDiTO6C7K4JsI1wMdAGdlYyuQkSAhuxnL3Ae+A64ZR4UyBHFgV7eT8N2dAJXgNXmL82mPwNOArO9i8kTIsAJtAwGrKZ/48MzrDMb+Fk/0E2vBjp8Ccc71pFNc930Vv9i8ZTtIEyv8zEQL1kLwvQCHwPxkoWAUadnFauOtYgSf2tY5enzjONYJRokMktyPJ/sS1dEa0MYsG1OChJrkdn/bdg4PnLsDcdPpxzH79hcxaL6oOP5/UCyN8Vfpya4NzpZNq0Tuc2JK9qWW+dqabSkubMQaw3x44GZKFFng+XSlhRt0zKrrFGR+aQu4Gqmk70pLl8VKVn/aYBE5wzC1RLhaondu6r44dCrsmvNFG36m40hwtVT19iqNpnDxyYK6h8+nrSsw2QfjNydNJZL3fwgSjTI4JBaVq2ZotPbnMrnLgqTX7aFir2UQe5m1KS4T/FStEWZrq2RaFSE6aMnJhh/oqX03DmBktajHxRlelNC3M3rg2mePs9wbUDMdHtraRtaIm7V9Q/mT8/paouKsqVJDL8xpJkduKGyNvvDbeWKEL/+/jbvNRqWyOzaIo4/mhcg1iomc3hEdVyT09GacW1aiQapmy/S98I/mulkX5p9bzLGDpqIy5ztcW4YFtUHHevu6JjKT7+8rojWjGvT5tTVU1unfyBtzHhrSyivaTuGR1Ru3k5z6mwqb2dXLq1r082m2tyoyHR32f9l1qjI1NZIjgGcPP3WVWkrt9aMq40sEZeZO8f9ntfRPv0OrZK4ik75XAwbHVPp65+avg1LZGO9xVeG8vbiflPQdG2NZGk7e/5N2RpSoioHv58JaJ2R27bUDwrmbEe7bGk79V07l8Eh1WhUADasKb1DqzQFTX/RPLUhceL2sKiRzcve3XUtZZ9wFFcn3mO6uyKSfqdf+hqJd7wGkd73fQzES/4DYbrHx0C85BwI03/4GIiX/AnC9AXgon+xeMIZsh7NJWsP8MyXcCrPI7RHtoDV9B3ga2Dc64gqzDiwGc0fMLU5uQSsQHuY/SFwHoih+TKwa5vuAGuA9cA2tNcvFlDCUxAfeIlWfvXXL/62G/Q/ynk4UBWNnikAAAAASUVORK5CYII=",
586
+ botVerif: "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAkCAYAAAANdf2OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFGUlEQVRoge2aa2wUVRiGn92dbXdLIIXKD1IF27TUMukllUqpFpTUcEmwxEuIGE1KIlEgoQmoQCRcRH6gVKJIIo0xQqMGEm6NpYnERDCyClZKaWltrUrTkCiFBkNvu7Prj2F2Znc67eytS5Y+SX/szPnOfn3POe/5zmktPp+Pl9f8h4ZUYD3wHCACKUwwFv1AG1AH7Af6lBdCUMOlwBEgbZwSSxRSgKJ7P+uB14B6AKum0XLkEZgQNzLSgNPIevoFTgW+AGzxySnhsCHrmaoIvJaJmRtt0oC1isAV8cwkgalQBM6KaxqJS7Yi8LS4ppG4TLWO3SY6rFiWNF5fdV8RXAfHhIolSaxckUxersBnXw7yz01vSPElRWqabZ0SfXd8hm3FHBuTJ1kM37saPTGJNcJy7yRnnHGE5M0R2Frl9H+WJDh0eJBzF9ym4kuKBDa8ocZ/c3yQUw3Gse9tTiErc/Rq03XRzYn6Ya73BA50JLFGxNQinA4Lb61zBjyz2WDmw+a/dt7jgYtsTk7ki66k2M6Od1IQc0Iv+0ONjalFbKlyYrcHPvul0UPtsSHTfRTkBaaYLwrMTLeamkGui25+/lVd1pmPWil/Ogmnw4LTYaFyVTKbtvdHPVZLzARe9UIy2UHL7eYtLwc+HzDdR8USO06H3hOfmifw1fHhMeP/7fUG+KarEbr+8votJ32GDTHHRku7FNVYLTGxiOxMG8sX66uGvZ8M4DZnvUCgHXx/ThX0yXn2kZqbInijKhDN20Q4sVEXOMkOuzbrbzhrjw3RbXJjAEidYiFfVAU+dnqYW7fl+GlTrWH5ZzyIusDbNunFvdYh8e13Yy9pLcvK1Vl6pcVD3x0fl5vVGVRaHJ67lZcFxjW1jL7EI40NKcstG5xMf8jKnv0D3OzVz8bVrzjIygjy3V4vez4aezMIpqhATa21XRa2uVVi0QL52fwn7NTUjr5Z5s4WWPW8+nl6mpWSYnXgOrskQw+NJFaLaYEXzLf7l2z1rkl88OkAza3qjJpbKPDsQr03flwziCfE+lzMsZE+Qx2oH36SO3A1elgz6PPv5OVlAmfPG3eelWkzrGt7bkjsO2i84UYSq8WURRSIAm9WOvyf7XbYWuVk5YpkQK5tN6516uIOHx2io8v8ElTQLn/FHhSaNDZRXBT6ZtfZJVHXMMTufQOjngijFWtqBjv12gHy/ULGLCupU/TjdKXVw5mzofmuQqGm9s0XBb4+NHnEdvmiQOoUi+EvW9cwZKqci3asFlMz2HXJw6btd2nv1M/GAlFg1iOB3UgSfHjAfL2rpbxMYFoId1ALS8flOiVsTGfXc8PLjr39vP6qg0Vloy/N96v7cYd+LwKA+JiaUs8NicYmfUe5swW/P5bNt496NxFvQh7+miODXOuQWLfaMeL7+rPDXOsI3XdBrn21R+PzF9wjiifmSLy7US4H02fYTB+d40FYdfCPLjdv77zLn9cDhbzaJnHkqPl7hmAWlgoBR2OlegimpV3yHzoAFj8T/sku1oR90Oju8bJ1d79/I/P5oNpk6WLE3EL94cKI3zX7QWHe/evDUbkPrliaxN/dXi5fDdN4E5ioDP2pM5GXM4nKuP1N7kFFEfhWXLNIXG4rAv8R1zQSl05F4JPxzCKBOakIfBDojWcmCUgvcFARuA+oBMI7gk0QjISsZ5+2iqgDXkTz39kThEUf8BKynroy7SSQAWwDLgGRHc0eHAaA34CdyPqdUF78DyvRxMINRXPNAAAAAElFTkSuQmCC",
587
+ frame: "iVBORw0KGgoAAAANSUhEUgAAA3UAAAEvCAYAAADihOiYAAABN2lDQ1BBZG9iZSBSR0IgKDE5OTgpAAAokZWPv0rDUBSHvxtFxaFWCOLgcCdRUGzVwYxJW4ogWKtDkq1JQ5ViEm6uf/oQjm4dXNx9AidHwUHxCXwDxamDQ4QMBYvf9J3fORzOAaNi152GUYbzWKt205Gu58vZF2aYAoBOmKV2q3UAECdxxBjf7wiA10277jTG+38yH6ZKAyNguxtlIYgK0L/SqQYxBMygn2oQD4CpTto1EE9AqZf7G1AKcv8ASsr1fBBfgNlzPR+MOcAMcl8BTB1da4Bakg7UWe9Uy6plWdLuJkEkjweZjs4zuR+HiUoT1dFRF8jvA2AxH2w3HblWtay99X/+PRHX82Vun0cIQCw9F1lBeKEuf1UYO5PrYsdwGQ7vYXpUZLs3cLcBC7dFtlqF8hY8Dn8AwMZP/fNTP8gAAAAJcEhZcwAACxMAAAsTAQCanBgAAAXAaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA3LjEtYzAwMCA3OS43YTdhMjM2LCAyMDIxLzA4LzEyLTAwOjI1OjIwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIyLjUgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMi0wNC0yOVQxOToyODowMi0wNTowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjItMDQtMjlUMjA6MTk6MzEtMDU6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjItMDQtMjlUMjA6MTk6MzEtMDU6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZTUwMDE2ZGYtZDZjNS1lYTQ5LWE4MWEtNGEzZGM5YzE4NWJjIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjY1NDcxQjFDQzgxQzExRUNCRjBEOTkzMEY5NTU3MTIwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NjU0NzFCMUNDODFDMTFFQ0JGMEQ5OTMwRjk1NTcxMjAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NjU0NzFCMTlDODFDMTFFQ0JGMEQ5OTMwRjk1NTcxMjAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NjU0NzFCMUFDODFDMTFFQ0JGMEQ5OTMwRjk1NTcxMjAiLz4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZTUwMDE2ZGYtZDZjNS1lYTQ5LWE4MWEtNGEzZGM5YzE4NWJjIiBzdEV2dDp3aGVuPSIyMDIyLTA0LTI5VDIwOjE5OjMxLTA1OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjIuNSAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+c9rTwAAADM5JREFUeJzt3W3ItvVdwPHvfa10zAcmtIjYA9ltk9GuIDVkmJSZuWER5AqC6F2QwbAWIsXAiCiJae9WjKAHRNpe1cYmexO4GrIHAkWZGck9x8i5OXG3s9zm3YvDGx+aXsd5ndd158/r84GLG+X//x3/8+WX4zzO41hnxrHq4uqy6p3VRdWPVD9QnV+de4bOAQAAcJBOVk9WX6serh6q7qs+V32xOnXYBzh2iLPfUF1X/VJ1TfWmQ7wWAADAq81j1aeqf6o+Xn3rMC5yGFF3SfU71XtzBw4AAKCWO3ofrT7UchfvwBxk1P1s9YHn/gUAAOB7u7u6pfrngxh2EFH3juqD1bUHMAsAAOCouKt6f/XANkNet8Xes1vq8u+rt29zCAAAgCPoePVbLW31r9V39zNkv3fqLqz+obp0n/sBAAB43uerX6v+c9ONO/u42PXVvyXoAAAADsqlLZ11/aYbN/365R+0/FrL6ze9EAAAAK/o7Ja3CHy7+vTaTWu/fnms5cdQfnfzc73IZ6p7qkeqL1dPtLyr4eSWcwEAAP4/nNvyju43Vm+u3lJdXr1ry7m3Vb/fAb68/Lbnhu3n70R1a3V1tXtQBwIAAHiV2m3pn1urh9t/S/3pQR3o5n0e4P7qxoQcAABwdO1W72vpo/101Y3bHuBXqmc3vOhj1U2JOQAAgNN2Wzrp0Tbrq2fbx4+nnHZhyzNvm1zwjupn9ntBAACA17grW7ppk856oqXPNnJWy3sSNrnQh3N3DgAAYC+7Lf20SW99oaXTVrtlg+FPVzck6AAAANbabemop1vfXresHX58g8FPV9dt/3kAAACOpF+uvtv6/jq+ZugnVg481VKWAAAA7N8NrW+wT+417IoNhnmGDgAAYHubPmN3xSsNW3uX7s4EHQAAwEHZbemsNT32iZcbcsnKAV+vrjqczwEAAHBkXdXSW2u67Ce/14C/Xrn5pkP8EAAAAEfZTa3rsr986cZzqpMrNj6Yr10CAAAclt2W7tqrzR6vzn7hxl9dscldOgAAgMO39m7d9S/cdMeKDSdylw4AAOCw7bb0116N9jenNxyrvrpiw5+foQ8AAABw1N3e3o32pdOLL16x+FR19Rk7PgAAwNF2bes67fhOddmKgZ9puZsHAADA4ftKdc+KdVfsVD+xYuE91b1bHQkAAIC17m25ubaXd+5Ux1csfGS78wAAALChEyvWXLRTvXXFwv/a8jAAAABsZs0jcG/eqS5YsfDJLQ8DAADAZtZ02AU71XkrFp7c8jAAAABsZk2Hnbc26r615WEAAADYzJoOO2+nOmvFwme2PAwAAACbWdNhZ+0c+jEAAAA4NKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYDBRBwAAMJioAwAAGEzUAQAADCbqAAAABhN1AAAAg4k6AACAwUQdAADAYKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYDBRBwAAMJioAwAAGEzUAQAADCbqAAAABhN1AAAAg4k6AACAwUQdAADAYKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYDBRBwAAMJioAwAAGEzUAQAADCbqAAAABhN1AAAAg4k6AACAwUQdAADAYKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYDBRBwAAMJioAwAAGEzUAQAADCbqAAAABhN1AAAAg4k6AACAwUQdAADAYKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYDBRBwAAMJioAwAAGEzUAQAADCbqAAAABhN1AAAAg4k6AACAwUQdAADAYKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYDBRBwAAMJioAwAAGEzUAQAADCbqAAAABhN1AAAAg4k6AACAwUQdAADAYKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYDBRBwAAMJioAwAAGEzUAQAADCbqAAAABhN1AAAAg4k6AACAwUQdAADAYKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYDBRBwAAMJioAwAAGEzUAQAADCbqAAAABhN1AAAAg4k6AACAwUQdAADAYKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYDBRBwAAMJioAwAAGEzUAQAADCbqAAAABhN1AAAAg4k6AACAwUQdAADAYKIOAABgMFEHAAAwmKgDAAAYTNQBAAAMJuoAAAAGE3UAAACDiToAAIDBRB0AAMBgog4AAGAwUQcAADCYqAMAABhM1AEAAAwm6gAAAAYTdQAAAIOJOgAAgMFEHQAAwGCiDgAAYLCd6pkV68467IMAAADwIms67Jmd6psrFr5hy8MAAACwmTUd9s21UXfulocBAABgM2ui7uRO9Y0VC8/f8jAAAABs5o0r1jy+U31pxcIf2u4sAAAAbOgHV6z58k71HysWvmXLwwAAALCZt61Y89BOde+KhZdXu9udBwAAgJV2q3etWHdf1cXVqRV/Vx/GSQEAAPg/rm1dpx3fqR6svrZi6M8fylEBAAB4qV9YseaRXvA43R3tXYAn8hVMAACAw7bb0l97NdrfVu08t+kfVwx+a3XNAR8WAACAF7umpb/28rEX/sc51cn2LsEHc7cOAADgsOy2dNdebfZ4dXY9f6fuqeojKy7wYy0P7AEAAHDwrmnprr18pPqfl/7PS1v36ypfr646mPMCAADwnKtaemtNl13yckPuWjngznwNEwAA4KDstnTWmh6765UGXbFyyKnqwwk7AACAbe229NXaFvvpvQZ+coNhNxzoRwEAADh6bmh9g73iXbrTLqqeXjnwqeo9B/ZRAAAAjpb3tHTVmv56uqXXVrll5dDTYXdDvooJAACw1m5LR60NulPVH21ygbOqL2ww3DN2AAAA62z6DN2plj47e9MLXVg9seGF7qiu3OcHAwAAeK27sqWbNumsJ6of3e8Fr6+e3fCCj1Y35a4dAADAabstnfRom/XVsy1dtpWbN7zo6b/7q/cl7gAAgKNrt6WL7m9/XXXzQR3k9n0e4FT1cHVrdXUCDwAAeO3bbemfW1t6aL8tdfuaix1beahj1W3VjSvXv5x/qT5bnai+Uv139WR1svrOlrMBAADOpO+rzq3Or15f/XD1tuqnqiu2nP0X1e+1xN0rWht1p/1h9cf72AcAAMDeTlUfqP5k7YbXbXiBT7d8F/Ta9vFzmgAAALysJ6vfqP5qk037veN2vLqzunSf+wEAAHje56tfrx7adOOmd+pOe7z6u2qnunyLOQAAAEfZd6o/q36z+up+BhzEs3HvqD7Y8pVMAAAA1rmren/1wDZDdg7gIA9U765+rrr7AOYBAAC8lt3d8sqDd7dl0NXh/IrlZdVvV+9t+XlPAACAo+5k9dHqQ9XnDnLwYb6a4JzquuoXq2uqNx3itQAAAF5tHqs+VX2s+nj11GFc5Ey9b+5YdXHLS/h+vHp7y0v5zq0ueO7f7z9DZwEAADgI3265A/eN5/49Uf17dV/12eqLrXh5+Lb+F2koYlXxK9cZAAAAAElFTkSuQmCC"
588
+ },
589
+ statusImgs: {
590
+ dnd: "iVBORw0KGgoAAAANSUhEUgAAAD4AAAA+CAYAAABzwahEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMS1jMDAwIDc5Ljk4ZDc5NDIsIDIwMjIvMDMvMjEtMTE6NDA6NTkgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCAyMi41IChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo3MzBGNUZCRjkyRTcxMUVEOERFOEY2OUQ5Nzg3MzEwOSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo3MzBGNUZDMDkyRTcxMUVEOERFOEY2OUQ5Nzg3MzEwOSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjczMEY1RkJEOTJFNzExRUQ4REU4RjY5RDk3ODczMTA5IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjczMEY1RkJFOTJFNzExRUQ4REU4RjY5RDk3ODczMTA5Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+ptCIzAAAAy1JREFUeNrsms9LVFEUx68hOqRT0B+QaWo/cFFYCxdBuu8HA0FWEFS0b1mGkWhRi/oHok2WaRD0A2yl7VpkJEhS9sv8AwJziJnBqO9hzlCLudP7ce679+k98N28N++e83lzf5zz7q37feCgWo+2Qa1T8+Ae3IN7cA++lqw+AR+7oV6oG9oJbYWaoc3QMpSHlqD30BtoGpo3HVSdoQSmBToLnYC2R3j+M/QAugstpgG8HboMnRTqTavQfWgY+uTiGM9AV6E56LTgEKrn9ua4/YxL4B3QK2hQMrAqL3aQ/XS4AE6T1mtoT0KTMfmZYb/WwHPQJM/OSdom9puzAd4HjUGNlpbhRvbflyQ4jbHHUIPlHKSB4+hMApze9LiF7q0ziuNhlEk1LPglaK9j2SfFc9FkAtPO62lGuWdFqCtMkhPmHx9yFLoyBIdMdPU26JjjBRfF1yoNfi6hSi5uenteGrw/JWV2vyT4LmhbSsBbuP4XAf9fdvSMf5OlVcKgsuzneYD6QQS8u8Y9qr0P81eTvOF/M89+DrFfne2TAt+huU5vfsRSlx7hnlbNOqXA2zTXb1kez7drjHMR8C2a6zOWwXX+s1LguipsxTL4So16XQS8pNJlP6TAv8fpUgatOU5PDAL+Jc6yYdD2a64vSYEvaK5fsAyu8/9BClw3e1IiMWAJeoD9R15tgoBP17hHOxxPOE1sMgzbxH6esl+dvZT8ArMYNDGwbN+CFlRBy9KxlCxlgeMMCn5HlTfwXLZfHKcoOG3bPnIcfILjFAUno027oqPQFNeVMA+EAadPtzccBb8JfTQFTnYdeusYNMVzLexDYcEL0HFVPrvigi1zPAXT4JUUNudA1VbiOBaiPBx1m3gKOmURvsT+p6I2EOdgAC1vR4LWv8L19tG4y2vcoyAvuDydTQh6lv1Nxm1I4vAPLSM9qrxpZ2qdL3L7PWGXLZPgldmeEgjaqr0nmN6ucntd3H5B6k2aOtlIFdIZnoBaIzz/FRpVKTrZWM1oL4u2fipnWam83aj+nmX9yeXkv2dZ35kOKomt33mVwKFcW2M8debBPbgH9+Ae3IOvAfsjwAD7bpEEX3YuqAAAAABJRU5ErkJggg==",
591
+ idle: "iVBORw0KGgoAAAANSUhEUgAAAD4AAAA+CAYAAABzwahEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMS1jMDAwIDc5Ljk4ZDc5NDIsIDIwMjIvMDMvMjEtMTE6NDA6NTkgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCAyMi41IChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2RTVCOEFCOTkyRTcxMUVEOTM3RERBMUE4RjdBMUM4MCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2RTVCOEFCQTkyRTcxMUVEOTM3RERBMUE4RjdBMUM4MCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZFNUI4QUI3OTJFNzExRUQ5MzdEREExQThGN0ExQzgwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjZFNUI4QUI4OTJFNzExRUQ5MzdEREExQThGN0ExQzgwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+WA4qawAAAwpJREFUeNrsmj1oFEEUxyd+RsFECaaNEQ0iEfwstItFbARFEBUE0UKIgtiJjWKw0UpFr1Fs/IhGsI1aJDaawlMDmuBd/C4V1FPQyxrR/2NeETTn7ey+ud3ZzIMfhLudt/NLsjszb6bu9001JWOaUl7ci3txL+7FvbjrMSPBe28DF8EP8B68AI/BABixffO6hCcwB8H5ST5/Ba6Dy+BtFsUpboAdFb4bB9fASfAya8/4IfDlP4/iHvAMnAD1WRL/AM5UuYaEj4FBsDRLb/UcCEJctxLkQUdWxD+CeyGvbQB9PCpkYhy/a3DtbNADNmZB/Inh9bPAbdDmuvhohDaNPBzWuyxeithuFTg6VefqR8ASV8UbY7Sll123q+JxJybbQauL4qsFVpr7XRTvFMixyzXxhWCTQJ4WsNwl8QM8IZGIDlfE6a99WDDfWlfEz4H5gvnapMT3WpSm0tNO4ZwtUuIX4i4BKwTlPGsh7zwp8TmgF3QJdq6Lc063IN4gJR5wB3O8Bm6O0almzpGzJE3xVUr804Sf6XksKF3/ajLoTBO3KVh4pv+Ob2EuClNefgA2TPL5T6WrJlQyegReg8/83QKwGKzjGVmn4DhdLQYr9Pef+W21KFZINBNsZtIUBal/9bxja/O8lPiAY+L3pcRpA++dI9LUz2HJKWuPI+Kh+xlW/JLSG3hpjl/cT1Fx2ra9lXLxXu5nqDDZJqYq5nOlC3tpizGwQhnU5k2WpbQ/fSqlf+3TynBDwvRgAO1YPFS6iJ+WeMoTrLJJI9NCRJnn2qWUSJe4P2XThlEqMEVeSwcJSwfcj2KUxlFLT/1gd4LyAd+/P2qCODU3Gt62hF3/Cq+3t8YdXuMWG+8oXdUcqpH0EN+vL24iiSorDSPrld60G7M4TnfzfUYlEkqVl+mtehy0gyuC09txztfO+ctSv0lbB/wWgX38AmqN0P4NuKocP9lIe1l0SGcNWKZ03Xuu0vvhNA5/5+XkxLOsw7Y7VYtDvCOqBodyk3rGnQsv7sW9uBf34l48A/FHgAEA3wORBIONQMEAAAAASUVORK5CYII=",
592
+ invisible: "iVBORw0KGgoAAAANSUhEUgAAAD4AAAA+CAYAAABzwahEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMS1jMDAwIDc5Ljk4ZDc5NDIsIDIwMjIvMDMvMjEtMTE6NDA6NTkgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCAyMi41IChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2OUE0QkE5MzkyRTcxMUVEOTRDMkI1MjkwRTA0RDVCNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2OUE0QkE5NDkyRTcxMUVEOTRDMkI1MjkwRTA0RDVCNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY5QTRCQTkxOTJFNzExRUQ5NEMyQjUyOTBFMDRENUI3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY5QTRCQTkyOTJFNzExRUQ5NEMyQjUyOTBFMDRENUI3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+5VhQjAAAA+9JREFUeNrsWl1LFGEUnkpWCVOoi+jLb63Ei0ITQgv0DyRCkBFJFN11URdGgRtuUGhQfyAiCrIPqEu70iAjUCNBlPI7k6ILA2uJdVXoHPZZiljsPTNnZladAw+CM+8555l9553nPe/ZcKH1lrUebaO1Ti0gHhAPiAfEA+JryTI8iFFOqCNUEvYR8gjZhFzCPCFKmCF8ILwj9BBGVivxfMJZwklC8Qr35QK7CIcJZ/D/CcIjwj3C9GqY6iWE+4RxQut/SK9kxRg/Bn8l6Uo8i9BGGCI0K86kDPgbgv+sdCJeSnhLCGsmluLBhhGnNB2I86LVTzjg0WLMcQYQ1zfijYQuLE5eWg7iNvpBvJ7QScj06TOcifj1XhLnd+w5IeSzBgkhjzIviPOTfuLD9F5JBzy2s6hKiV8lHHSY7DLhM2ESf5cc+uN8rrhJnEVEi83k5gh3CEcgV/MgUpLy9Siuz9n0f1kqciTEIzam1CLhOqGAcInQS4j9c88C4TWuF+D+RRuvYMQN4kWE48JkvhJqIDyihmOiuL8G4yXG+RVqEz8nlKGzhFqIGzvWj9diVihvz2sTbxIkEIe4mHS4aE3AT1wwpkmT+H68e6bW4eCXTvXLdwi3w+VaxCXqiFflduVvdbtwta/TIl4pCPpAsJCZWhR+Ta1Ki/heQdAXLik0id8yLeJFhgFZgfW5RLxPoPDytYhvNQz4DWLEDVuAfxPbokXcdBf2w+UNian/HC3icc2ADgsQag/IhPh3w4DbXSxMZMK/if3UIm6qwFgyVrtEvFogmWe0iI8KEmxwibjE70ct4gOCoM3YX2taNvya2oAW8R5B0G0oCmhaC/ya2ist4nyA90mYaJUS6UPCB8l5DmtuSzsFwUOQmIUOSbNilFZzjfM0JX7XkhUFdxPeOPjleVwv/JjaMvJUJc5FgWfC5HeAfJtgwcvG/b0YL7GnyFOVOFvYhhYPYdw04baVKEdlpRAntbg+jfulQojzuiYZIKmjjaMoELYxdXlVvgjwlPxiJSqpHH+n5fxYmas0Y5IB0gOFm4T3DpPcRNiDxStPgTTnc0M6SEqca+InrETvSjrYPPKJuU08KWGl1U83LFnNHbUz2O4xcTfhlI/k44jfbdeBk8YA/rwd86AAkWq/3WDj86pGnO0lxMagR6QHEa/LqSON5h/+jHCPWsRyt+YWQZwxDYda7V4xCIgKwkPL+Zl30pbgrwL+Y1pPUrvBj0XOaSvRLsLHvVM2/UxhfCn8jWtPIbdaOpPSk8FnWXwMlexl5br3ZutPL+svbCf/7mUddnux8KKJd8TyoCnX76m+aiwgHhAPiAfEA+IB8TVgvwUYAD4Gvbi4ORocAAAAAElFTkSuQmCC",
593
+ online: "iVBORw0KGgoAAAANSUhEUgAAAD4AAAA+CAYAAABzwahEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMS1jMDAwIDc5Ljk4ZDc5NDIsIDIwMjIvMDMvMjEtMTE6NDA6NTkgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCAyMi41IChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2NThGMDEwODkyRTcxMUVEQTY1MDhDRDFERjZDQUE5NCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2NThGMDEwOTkyRTcxMUVEQTY1MDhDRDFERjZDQUE5NCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY1OEYwMTA2OTJFNzExRURBNjUwOENEMURGNkNBQTk0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY1OEYwMTA3OTJFNzExRURBNjUwOENEMURGNkNBQTk0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+tV936QAAAsVJREFUeNrsm99LVEEUx2dDVgk16A9wS12z8KGwFx/dfyBZiDKkHpL+BKMgI4Oi/oaQIFJT8NWetLce2kgQpR+m5R8QWBHrYtD3cM+SDwp77z0zd47Ogc/jzJzP5d6ZMzuzOfPyijmKccwc0QjiQTyIB/EgfpiiycEY58Ag6Ae9oAO0ghNgG/wGW+AjeA+WwJrtpHKWCpgCuAmuga4E7b+CKTAJvml41bvBc7AO7iWUNtyO2n/h/rp9FW8BD8AKuCH4CTVxfyvcf4tP4kXwFoxLJrbPgx3ncYo+iNOk9Q6cdzQZ0zgVHjcz8TJY4NnZZbTzuOUsxEtgGjRntAw38/gll+L0jc2DfMY1SJ7z6HEhTk/6VQav90FBecwkmVTjit8FFzyrPimfOzbFqYgY87T0vh23yIkjPmFxnZaY7CZsiHeCy55vuCi/09Lio452cmnL21vS4sNKttnDkuJnwSkl4gXe/4uIl4yuGJQS71cmflFK/Iwy8R4p8U5l4gUp8ZPKxNukxPPKxNulxGvKxH9Kif9QJv5LSnxDmfiWlPhnZeKfpMQrysQrUuJLysTfSInTAd53JdKU56rktnRaiXjDeTYq/gzsei79l/MUFadj2znPxWc5T1FxCjq02/FUmvK6H6dBHHE6837iqfhTE52lWxGneAw+eCZN+TyK2yiueBVcNdHdFR9im/Op2havl7BlD3ZtNc4jUUmd9Jh4EYxkKF/j8ReTdpDmYgAtb5ca3f8K77eH0i6vaa+CvDbRr5rLjqSXebyFtB1JXP6hZWTARId2ttb5He5/IO6yZVO8PttTAdEHXgiWt7vcXx/3X5V6ktIX/KjIuW6i6yIPwWbCfja5fZH7W5d+hXIO/pNCZ1l0DFW/y0q/ex83/++y/uHt5N67rKu2k3Jx9LtmHFzKzfpVVxNBPIgH8SAexIP4IYh/AgwAUZF3I1xau4QAAAAASUVORK5CYII=",
594
+ phone: "iVBORw0KGgoAAAANSUhEUgAAACkAAAA+CAYAAAC2szLVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMS1jMDAwIDc5Ljk4ZDc5NDIsIDIwMjIvMDMvMjEtMTE6NDA6NTkgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCAyMi41IChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4QkI3RjFBQTkzQUUxMUVEQjYwRkNCMjA5OTlGRUI1RCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4QkI3RjFBQjkzQUUxMUVEQjYwRkNCMjA5OTlGRUI1RCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjhCQjdGMUE4OTNBRTExRURCNjBGQ0IyMDk5OUZFQjVEIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjhCQjdGMUE5OTNBRTExRURCNjBGQ0IyMDk5OUZFQjVEIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8++UgzsQAAA8dJREFUeNrsmsuPDEEcx7/9m269s2tY70dCsgfxOtiseB4kIqw4sA4iToiLvfgDXEREXASJCyKckTghuBIn8YjEI8LKkgjWWvuanZnuKr/qHmzYx+xUdfVE/JLqzPSmf/WZ36uq91cO7qwHhnIABUBhMtDbuAkNA7shMs0AFvNwYEe64YgnCL37COVZTPtcQCkLCGKAu2sYrh4o8RDeJRSy+0ECqYpAJ+ry7ZDiFkiCINz4D/kpDyLATIDUhbAQQw03IfydKPpw0TsfkLjMl3WKGpJQE6K8GXrXUfKaiAGbEXj7IsCaE2byC+cIVGyPvtSqCKeVUKxbm3qijC2DbEk0obYlVFmSqXFIKMiw1iHdUe6f5PHB4mrzu4wDe3m0jA/pOEcg5aB1kzmRTRbz3C1/unskWVR+wDagksaRYnLsB23AjjMHmVCSJGDlkEmBVqiTklBqWhclqdyUDrIxie6zZHOyan8cWXObhvXJSnxpxjElnqkGEs3cC81IMIZKltm3ruFQBmuqm9Yqkp4lE9yZG34LlfGoWcjhcAZBKRFAw6CUGKBBUEoU0BAoJQ5oAJSsAGqCkjVAjWfJKmCVOsg6YBW6KBXACeqk1AAnoJtSBaxwjtEgX1kD/Bv0W2X7SSlP8/WT9T1ZDLqh0k3vwX97P/kf0oSIsjrhxKP2XsQYyis+Rei+gBtk+cYWBG7WRKdN35Kx1Z5DyM3I9qv28x74A23w800MfgKOTNmSCtALXoOCFSh4wS8XR01U+QmhOMwh8A6uPJ+iJR3VltwORwTsZv6s/seuuhzqjTHDP8JXPfQLDHpbx6LVWzJgiPqB+8h1vYzgcl9jwJ5ZQF0/ML2T73Uj6p8X6k+ga+5WuMIypEoIQY+iUwc/55auSp74JELPvLj5qywYum90EkgjJhVkhiIgWY5FBeSWEJ1G6M8NDyZHF9Kp2pJBZiOXmdj1CtBTbUpRtiwNq5VytU7063VpCctR8nZg5kdg6heg9FMVU2ZEDK1iknBML7sFPuuVIfcK18eVmDSk3D9CVDgX+bpMD9INn2ktYX7eR9eCh+idfZSTZmnZOw0csjtZ7z3+ekCzluccXGvbhaDuatXnMFTZCcv5p1wr6X0EyUXIyGpb8joIPdOuoeS+jItwNUlO8fEYNeLjOguMASoP57oPEXLfuSj3beMJQpM7FyM7qkx4Hn7fDcKct8Dkrg5ef1dxWem0f1hgtGwJzsAtHET/DK6TRS7GwSRVhB8jxBKOgeMc+q1lt9mWPBvpCYfeKVB4J1okOM5/CDAAGWZFCtyn42oAAAAASUVORK5CYII=",
595
+ streaming: "iVBORw0KGgoAAAANSUhEUgAAAD4AAAA+CAYAAABzwahEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMS1jMDAwIDc5Ljk4ZDc5NDIsIDIwMjIvMDMvMjEtMTE6NDA6NTkgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCAyMi41IChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1RkRGQUY4RTkyRTcxMUVEOTAwNEM4Qzk3MjIyQTAzRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1RkRGQUY4RjkyRTcxMUVEOTAwNEM4Qzk3MjIyQTAzRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjVGREZBRjhDOTJFNzExRUQ5MDA0QzhDOTcyMjJBMDNFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjVGREZBRjhEOTJFNzExRUQ5MDA0QzhDOTcyMjJBMDNFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+4FcVmgAAA6ZJREFUeNrsmktsjFEUx2+laUtQERuRFG2nqC7I2NjprFhhEqLSECoWrNQrXiPGKwQ7EgkikRiPhOXY0FpZGFGpFi1as2AjoghjUo//yZyGhYzvce6937TfSX6bZr5zz//rvd8959xb1hY9r8aijVNj1ELhofBQeCg8FD6arNzAGI2gGUTBXFADJoJqMAS+gCx4Dh6BDtBbqsJngjawFtQV+V01MwMsBhv476/AVXAJDJbCVK8Hl8FLcOA/ootZHT/fz/7qgyq8ChwC3WC94EwqZ3/d7L8qSMIj4AFISAb2jxeb4HEiQRBOH62HYIGhjzGNk+FxrQmPgzR/nEzaZB43bkN4DKRApaVtuJLHj5kUTmvsFqiwnINUcBwNJoTTm75uYXoXywOuefmouhW+FywMWPZJ8ezRKZySiF0BTb13u01y3AhPephSy0zk3bwEkzqE14JVHgK6w1NxB/ioWTzFN1ta+CYfaWgenObd4BwY1lhwbZYW3iIQ2HuwlWdApybxLZLC54FZgsE95ZQzzlWcdDncKCU8pum/cxvMBzu5GSFlzVLCoxo/SLT+T3H2RU2HnwI+F0kJn2NgO3rHHRsK+r5PXw1SwmsNJiKPeapSyyrrY52LCJ9qOAv7xZUXNSb3e1j/k6SE26rCvoGjvNRS/EKc1usiwvOW8/C3PPXbHf7+k5TwD5aFTwdXwBmHv/8sJfy1JcHjwT7QB1pBmcPnslLC+yyIXq0KJytHVOHUxY29kBKeMdxUoCMk6vLUePSRkRLeYWgdX+Sgl/j01SklnBoJbzQJpq1yO3gGNir/fX6Ks0eyLE1pEL2Sg6RcXap56ThOp8IvCDYQqGy8qwqtYcnDwB8cp6hwOra96TOwaeAseKKp1L3BcYoKJ6NDu+8eAqKW0DZVOPLdovScyVNcB9084EY4dUtOuAxoKf+HKeuaonFXOMkvVotwsuNcOjq1tHLYCvJZyh5z+5Bb4TmwRhXurgTBhjienG7hIylsPABVW57j8JRSe00Y7nHhkLcoupXjUCaFK97eljutfwWNxlvhd3v1myLSERE1CLsMie7i8dJ+HUlc/qFthO6oJT3u80736SSP0y/hUOq6V44TiCbulkilt8Psr4n956TepPQFP0py1qnCAeFhMODRzwA/H2F/0kdN2q50DnKKm+AEhnLzkbus1PeeoP7cZf3K5eTfd1l7dH8sTFzi7VVmLgdYneolY6HwUHgoPBQeCg+FjwL7LcAAc/CpEwrVegIAAAAASUVORK5CYII="
596
+ }
597
+ };
598
+
599
+ // Src/Utils/ProfileImage/constants.ts
600
+ var { otherImgs, statusImgs } = ProfileImage_files_default;
601
+ var alphaValue = 0.4;
602
+ var clydeID = "1081004946872352958";
603
+
604
+ // Src/Utils/ProfileImage/badges.ts
605
+ async function getBadges(data, options) {
606
+ const { assets } = data;
607
+ const badges = [...assets?.badges || []];
608
+ const canvasBadges = [];
609
+ for (const badge of badges.reverse()) {
610
+ const { name, icon } = badge;
611
+ const canvas = await loadImage2(icon).catch(() => void 0);
612
+ if (!canvas) {
613
+ throw new KiraError(
614
+ `Could not load badge: (${name})
615
+ If you think it is not a network problem, please report it in our discord: https://discord.gg/csedxqGQKP`,
616
+ "ASSET_LOAD" /* AssetLoad */
617
+ );
618
+ }
619
+ canvasBadges.push({ canvas, x: 0, y: 15, w: 60 });
620
+ }
621
+ if (options?.customBadges?.length) {
622
+ if (options?.overwriteBadges) {
623
+ canvasBadges.splice(0, badges.length);
624
+ }
625
+ for (let i = 0; i < options.customBadges.length; i++) {
626
+ const canvas = await loadImage2(parsePng(options.customBadges[i])).catch(() => void 0);
627
+ if (!canvas) {
628
+ const truncatedBadge = truncateText(options.customBadges[i], 30);
629
+ throw new KiraError(
630
+ `Could not load custom badge: (${truncatedBadge}), make sure that the image exists.`,
631
+ "ASSET_LOAD" /* AssetLoad */
632
+ );
633
+ }
634
+ canvasBadges.push({ canvas, x: 10, y: 22, w: 46 });
635
+ }
636
+ }
637
+ return canvasBadges;
638
+ }
639
+ async function genBadges(badges) {
640
+ const canvas = createCanvas2(885, 303);
641
+ const ctx = canvas.getContext("2d");
642
+ let x = 800;
643
+ for (const badge of badges) {
644
+ const { canvas: badgeCanvas, x: bX, y, w } = badge;
645
+ const ratio = badgeCanvas.height / badgeCanvas.width;
646
+ const drawH = w * ratio;
647
+ ctx.drawImage(badgeCanvas, x + bX, y + (w - drawH) / 2, w, drawH);
648
+ x -= 59;
649
+ }
650
+ return canvas;
651
+ }
652
+ async function genBotVerifBadge(data, badgeX, badgeY) {
653
+ const { basicInfo } = data;
654
+ const { id, verified } = basicInfo;
655
+ const canvas = createCanvas2(885, 303);
656
+ const ctx = canvas.getContext("2d");
657
+ const isClyde = id === clydeID;
658
+ const badgeName = isClyde ? "botAI" : verified ? "botVerif" : "botNoVerif";
659
+ const botBadgeBase64 = otherImgs[badgeName];
660
+ const botBagde = await loadImage2(Buffer.from(botBadgeBase64, "base64"));
661
+ ctx.drawImage(botBagde, badgeX, badgeY);
662
+ return canvas;
663
+ }
664
+
665
+ // Src/Utils/ProfileImage/frame.ts
666
+ import { createCanvas as createCanvas3, loadImage as loadImage3 } from "@napi-rs/canvas";
667
+ async function genBase(options, avatarData, bannerData) {
668
+ const canvas = createCanvas3(885, 303);
669
+ const ctx = canvas.getContext("2d");
670
+ let isBannerLoaded = true;
671
+ let cardBackground = await loadImage3(
672
+ options?.customBackground ? parseImg(options.customBackground) : bannerData ?? avatarData
673
+ ).catch(() => void 0);
674
+ if (!cardBackground) {
675
+ cardBackground = await loadImage3(avatarData).catch(() => void 0);
676
+ isBannerLoaded = false;
677
+ }
678
+ const condAvatar = options?.customBackground ? true : !isBannerLoaded ? false : bannerData !== null;
679
+ const wX = condAvatar ? 885 : 900;
680
+ const wY = condAvatar ? 303 : wX;
681
+ const cY = condAvatar ? 0 : -345;
682
+ ctx.fillStyle = "#18191c";
683
+ ctx.beginPath();
684
+ ctx.fillRect(0, 0, 885, 303);
685
+ ctx.fill();
686
+ ctx.filter = (options?.moreBackgroundBlur ? "blur(9px)" : options?.disableBackgroundBlur ? "blur(0px)" : "blur(3px)") + (options?.backgroundBrightness ? ` brightness(${options.backgroundBrightness + 100}%)` : "");
687
+ ctx.drawImage(cardBackground, 0, cY, wX, wY);
688
+ ctx.globalAlpha = 0.2;
689
+ ctx.fillStyle = "#2a2d33";
690
+ ctx.beginPath();
691
+ ctx.fillRect(0, 0, 885, 303);
692
+ ctx.fill();
693
+ return canvas;
694
+ }
695
+ async function genFrame(badges, options) {
696
+ const canvas = createCanvas3(885, 303);
697
+ const ctx = canvas.getContext("2d");
698
+ const cardFrame = await loadImage3(Buffer.from(otherImgs.frame, "base64"));
699
+ ctx.globalCompositeOperation = "source-out";
700
+ ctx.globalAlpha = 0.5;
701
+ ctx.drawImage(cardFrame, 0, 0, 885, 303);
702
+ ctx.globalCompositeOperation = "source-over";
703
+ ctx.globalAlpha = alphaValue;
704
+ ctx.fillStyle = "#000";
705
+ ctx.beginPath();
706
+ ctx.roundRect(696, 248, 165, 33, [12]);
707
+ ctx.fill();
708
+ ctx.globalAlpha = 1;
709
+ const badgesLength = badges.length;
710
+ if (options?.badgesFrame && badgesLength > 0 && !options?.removeBadges) {
711
+ ctx.fillStyle = "#000";
712
+ ctx.globalAlpha = alphaValue;
713
+ ctx.beginPath();
714
+ ctx.roundRect(857 - badgesLength * 59, 15, 59 * badgesLength + 8, 61, [17]);
715
+ ctx.fill();
716
+ }
717
+ return canvas;
718
+ }
719
+ async function genBorder(borderColors, options) {
720
+ const canvas = createCanvas3(885, 303);
721
+ const ctx = canvas.getContext("2d");
722
+ if (borderColors.length > 20)
723
+ throw new KiraError(
724
+ `Invalid borderColor length (${borderColors.length}) must be a maximum of 20 colors`
725
+ );
726
+ const gradX = options.borderAllign == "vertical" ? 0 : 885;
727
+ const gradY = options.borderAllign == "vertical" ? 303 : 0;
728
+ const grd = ctx.createLinearGradient(0, 0, gradX, gradY);
729
+ for (let i = 0; i < borderColors.length; i++) {
730
+ const stop = borderColors.length === 1 ? 0 : i / (borderColors.length - 1);
731
+ grd.addColorStop(stop, parseHex(borderColors[i]));
732
+ }
733
+ ctx.fillStyle = grd;
734
+ ctx.beginPath();
735
+ ctx.fillRect(0, 0, 885, 303);
736
+ ctx.globalCompositeOperation = "destination-out";
737
+ ctx.beginPath();
738
+ ctx.roundRect(9, 9, 867, 285, [25]);
739
+ ctx.fill();
740
+ return canvas;
741
+ }
742
+
743
+ // Src/Utils/ProfileImage/avatar.ts
744
+ import { createCanvas as createCanvas4, loadImage as loadImage4 } from "@napi-rs/canvas";
745
+ async function genNameplate(data) {
746
+ const canvas = createCanvas4(885, 303);
747
+ const ctx = canvas.getContext("2d");
748
+ const nameplate = data?.decoration?.nameplate;
749
+ if (!nameplate) return canvas;
750
+ const image = await loadImage4(nameplate.imageURL).catch(() => void 0);
751
+ if (!image) return canvas;
752
+ ctx.save();
753
+ ctx.beginPath();
754
+ ctx.roundRect(280, 20, 585, 260, [20]);
755
+ ctx.clip();
756
+ ctx.globalAlpha = 0.5;
757
+ ctx.drawImage(image, 280, 20, 585, 260);
758
+ ctx.restore();
759
+ return canvas;
760
+ }
761
+ async function genAvatarFrame(data, options) {
762
+ let canvas = createCanvas4(885, 303);
763
+ const ctx = canvas.getContext("2d");
764
+ const frameUrl = data?.decoration?.avatarFrame;
765
+ const avatarFrame = await loadImage4(frameUrl);
766
+ ctx.drawImage(avatarFrame, 25, 18, 269, 269);
767
+ if (options?.presenceStatus) {
768
+ canvas = await cutAvatarStatus(canvas, options);
769
+ }
770
+ return canvas;
771
+ }
772
+ async function cutAvatarStatus(canvasToEdit, options) {
773
+ const canvas = createCanvas4(885, 303);
774
+ const ctx = canvas.getContext("2d");
775
+ const cX = options.presenceStatus == "phone" ? 224.5 : 212;
776
+ const cY = options.presenceStatus == "phone" ? 202 : 204;
777
+ ctx.drawImage(canvasToEdit, 0, 0);
778
+ ctx.globalCompositeOperation = "destination-out";
779
+ if (options.presenceStatus == "phone") ctx.roundRect(cX - 8, cY - 8, 57, 78, [10]);
780
+ else ctx.roundRect(212, 204, 62, 62, [62]);
781
+ ctx.fill();
782
+ ctx.globalCompositeOperation = "source-over";
783
+ return canvas;
784
+ }
785
+ async function genStatus(canvasToEdit, options) {
786
+ const canvas = createCanvas4(885, 303);
787
+ const ctx = canvas.getContext("2d");
788
+ const validStatus = ["idle", "dnd", "online", "invisible", "offline", "streaming", "phone"];
789
+ if (!validStatus.includes(options.presenceStatus))
790
+ throw new KiraError(
791
+ `Invalid presenceStatus ('${options.presenceStatus}') must be 'online' | 'idle' | 'offline' | 'dnd' | 'invisible' | 'streaming' | 'phone'`
792
+ );
793
+ const statusString = options.presenceStatus == "offline" ? "invisible" : options.presenceStatus;
794
+ const status = await loadImage4(Buffer.from(statusImgs[statusString], "base64"));
795
+ const cX = options.presenceStatus == "phone" ? 224.5 : 212;
796
+ const cY = options.presenceStatus == "phone" ? 202 : 204;
797
+ ctx.drawImage(canvasToEdit, 0, 0);
798
+ ctx.globalCompositeOperation = "destination-out";
799
+ if (options.presenceStatus == "phone") ctx.roundRect(cX - 8, cY - 8, 57, 78, [10]);
800
+ else ctx.roundRect(212, 204, 62, 62, [62]);
801
+ ctx.fill();
802
+ ctx.globalCompositeOperation = "source-over";
803
+ ctx.drawImage(status, cX, cY);
804
+ return canvas;
805
+ }
806
+
807
+ // Src/Utils/ProfileImage/textAvatar.ts
808
+ import { createCanvas as createCanvas5, loadImage as loadImage5 } from "@napi-rs/canvas";
809
+ async function genTextAndAvatar(data, options, avatarData) {
810
+ const { basicInfo } = data;
811
+ const { globalName, username: rawUsername, discriminator, bot, createdTimestamp, id } = basicInfo;
812
+ const isClyde = id === clydeID;
813
+ const pixelLength = bot ? 470 : 555;
814
+ let canvas = createCanvas5(885, 303);
815
+ const ctx = canvas.getContext("2d");
816
+ const fixedUsername = options?.customUsername || globalName || rawUsername;
817
+ const { username, newSize } = parseUsername(
818
+ fixedUsername,
819
+ ctx,
820
+ "Helvetica Bold",
821
+ "80",
822
+ pixelLength
823
+ );
824
+ if (options?.customSubtitle && !options.rankData) {
825
+ ctx.globalAlpha = alphaValue;
826
+ ctx.fillStyle = "#2a2d33";
827
+ ctx.beginPath();
828
+ ctx.roundRect(304, 248, 380, 33, [12]);
829
+ ctx.fill();
830
+ ctx.globalAlpha = 1;
831
+ ctx.font = withFallback("23px Helvetica");
832
+ ctx.textAlign = "left";
833
+ ctx.fillStyle = options?.color ? options.color : "#dadada";
834
+ ctx.fillText(`${options?.customSubtitle}`, 314, 273);
835
+ }
836
+ const createdDateString = getDateOrString(
837
+ options?.customDate,
838
+ createdTimestamp,
839
+ options?.localDateType
840
+ );
841
+ if (isClyde && !options?.customTag) {
842
+ options.customTag = "@clyde";
843
+ }
844
+ const tag = options?.customTag ? isString(options.customTag, "customTag") : !discriminator ? `@${rawUsername}` : `#${discriminator}`;
845
+ ctx.font = withFallback(`${newSize}px Helvetica Bold`);
846
+ ctx.textAlign = "left";
847
+ ctx.fillStyle = options?.usernameColor ? parseHex(options.usernameColor) : "#FFFFFF";
848
+ ctx.fillText(username, 300, 155);
849
+ const usernameWidth = ctx.measureText(username).width;
850
+ let verifBadgeX = 300 + usernameWidth + 10;
851
+ let verifBadgeY = 110;
852
+ const useCompactTag = options?.compactTag ?? bot;
853
+ if (useCompactTag) {
854
+ const tagFontSize = Math.round(newSize * 0.32);
855
+ const tagX = 300 + usernameWidth + 8;
856
+ if (bot) {
857
+ const pillHeight = 36;
858
+ const gap = 6;
859
+ const tagCapHeight = tagFontSize * 0.716;
860
+ const usernameCenterY = 155 - newSize * 0.358;
861
+ const blockHeight = pillHeight + gap + tagCapHeight;
862
+ const blockTop = Math.round(usernameCenterY - blockHeight / 2);
863
+ verifBadgeX = tagX;
864
+ verifBadgeY = blockTop;
865
+ const tagY = Math.round(blockTop + pillHeight + gap + tagCapHeight);
866
+ ctx.font = withFallback(`${tagFontSize}px Helvetica Bold`);
867
+ ctx.fillStyle = options?.tagColor ? parseHex(options.tagColor) : "#dadada";
868
+ ctx.fillText(tag, tagX, tagY);
869
+ if (!options?.removeServerTag && data?.decoration?.serverTag) {
870
+ await genServerTag(
871
+ ctx,
872
+ data.decoration.serverTag,
873
+ tagX + ctx.measureText(tag).width,
874
+ tagY + 24
875
+ );
876
+ }
877
+ } else {
878
+ const tagY = 155 - Math.round(newSize * 0.42);
879
+ ctx.font = withFallback(`${tagFontSize}px Helvetica Bold`);
880
+ ctx.fillStyle = options?.tagColor ? parseHex(options.tagColor) : "#dadada";
881
+ ctx.fillText(tag, tagX, tagY);
882
+ const tagWidth = ctx.measureText(tag).width;
883
+ verifBadgeX = tagX + tagWidth + 10;
884
+ if (!options?.removeServerTag && data?.decoration?.serverTag) {
885
+ await genServerTag(ctx, data.decoration.serverTag, tagX + tagWidth, tagY + 24);
886
+ }
887
+ }
888
+ } else if (!options?.rankData) {
889
+ ctx.font = withFallback("60px Helvetica");
890
+ ctx.fillStyle = options?.tagColor ? parseHex(options.tagColor) : "#dadada";
891
+ ctx.fillText(tag, 300, 215);
892
+ if (!options?.removeServerTag && data?.decoration?.serverTag) {
893
+ await genServerTag(ctx, data.decoration.serverTag, 300 + ctx.measureText(tag).width, 215);
894
+ }
895
+ }
896
+ ctx.font = withFallback("23px Helvetica");
897
+ ctx.textAlign = "center";
898
+ ctx.fillStyle = "#dadada";
899
+ ctx.fillText(createdDateString, 775, 273);
900
+ const cardAvatar = await loadImage5(avatarData);
901
+ const roundValue = options?.squareAvatar ? 30 : 225;
902
+ ctx.beginPath();
903
+ ctx.roundRect(47, 39, 225, 225, [roundValue]);
904
+ ctx.clip();
905
+ ctx.fillStyle = "#292b2f";
906
+ ctx.beginPath();
907
+ ctx.roundRect(47, 39, 225, 225, [roundValue]);
908
+ ctx.fill();
909
+ ctx.drawImage(cardAvatar, 47, 39, 225, 225);
910
+ ctx.closePath();
911
+ if (options?.presenceStatus) {
912
+ canvas = await genStatus(canvas, options);
913
+ }
914
+ return { canvas, verifBadgeX, verifBadgeY };
915
+ }
916
+ async function genServerTag(ctx, serverTag, anchorX, baselineY) {
917
+ const { text, badgeURL } = serverTag;
918
+ const label = text.toUpperCase();
919
+ ctx.font = withFallback("20px Helvetica Bold");
920
+ const labelWidth = ctx.measureText(label).width;
921
+ const badgeSize = badgeURL ? 18 : 0;
922
+ const paddingX = 10;
923
+ const gap = badgeURL ? 6 : 0;
924
+ const pillHeight = 28;
925
+ const pillWidth = paddingX * 2 + badgeSize + gap + labelWidth;
926
+ const pillX = Math.max(10, Math.min(anchorX + 14, 885 - pillWidth - 10));
927
+ const pillY = baselineY - pillHeight + 6;
928
+ ctx.globalAlpha = alphaValue;
929
+ ctx.fillStyle = "#000";
930
+ ctx.beginPath();
931
+ ctx.roundRect(pillX, pillY, pillWidth, pillHeight, [10]);
932
+ ctx.fill();
933
+ ctx.globalAlpha = 1;
934
+ let cursorX = pillX + paddingX;
935
+ const badgeImage = badgeURL ? await loadImage5(badgeURL).catch(() => void 0) : void 0;
936
+ if (badgeImage) {
937
+ ctx.drawImage(badgeImage, cursorX, pillY + (pillHeight - badgeSize) / 2, badgeSize, badgeSize);
938
+ cursorX += badgeSize + gap;
939
+ }
940
+ ctx.textAlign = "left";
941
+ ctx.fillStyle = "#dadada";
942
+ ctx.fillText(label, cursorX, pillY + pillHeight / 2 + 7);
943
+ }
944
+
945
+ // Src/Utils/ProfileImage/xpBar.ts
946
+ import { createCanvas as createCanvas6 } from "@napi-rs/canvas";
947
+ function genXpBar(options) {
948
+ const { currentXp, requiredXp, level, rank, barColor, levelColor, autoColorRank } = options.rankData;
949
+ if (isNaN(currentXp) || isNaN(requiredXp) || isNaN(level)) {
950
+ throw new KiraError("rankData options requires: currentXp, requiredXp and level properties");
951
+ }
952
+ const canvas = createCanvas6(885, 303);
953
+ const ctx = canvas.getContext("2d");
954
+ const mY = 8;
955
+ ctx.fillStyle = "#000";
956
+ ctx.globalAlpha = alphaValue;
957
+ ctx.beginPath();
958
+ ctx.roundRect(304, 248, 380, 33, [12]);
959
+ ctx.fill();
960
+ ctx.globalAlpha = 1;
961
+ const rankString = !isNaN(rank) ? `RANK #${abbreviateNumber(isNumber(rank, "rankData:rank"))}` : "";
962
+ const lvlString = !isNaN(level) ? `Lvl ${abbreviateNumber(isNumber(level, "rankData:level"))}` : "";
963
+ ctx.font = withFallback("21px Helvetica");
964
+ ctx.textAlign = "left";
965
+ ctx.fillStyle = "#dadada";
966
+ ctx.fillText(`${abbreviateNumber(currentXp)} / ${abbreviateNumber(requiredXp)} XP`, 314, 273);
967
+ const rankColors = {
968
+ gold: "#F1C40F",
969
+ silver: "#a1a4c9",
970
+ bronze: "#AD8A56",
971
+ current: "#dadada"
972
+ };
973
+ const rankMapping = {
974
+ "RANK #1": rankColors.gold,
975
+ "RANK #2": rankColors.silver,
976
+ "RANK #3": rankColors.bronze
977
+ };
978
+ if (autoColorRank && Object.prototype.hasOwnProperty.call(rankMapping, rankString)) {
979
+ rankColors.current = rankMapping[rankString];
980
+ }
981
+ ctx.font = withFallback("bold 21px Helvetica");
982
+ ctx.textAlign = "right";
983
+ ctx.fillStyle = rankColors.current;
984
+ ctx.fillText(`${rankString}`, 674 - ctx.measureText(lvlString).width - 10, 273);
985
+ ctx.font = withFallback("bold 21px Helvetica");
986
+ ctx.textAlign = "right";
987
+ ctx.fillStyle = levelColor ? parseHex(levelColor) : "#dadada";
988
+ ctx.fillText(`${lvlString}`, 674, 273);
989
+ ctx.globalAlpha = alphaValue;
990
+ ctx.fillStyle = "#000";
991
+ ctx.beginPath();
992
+ ctx.roundRect(304, 187 - mY, 557, 36, [14]);
993
+ ctx.fill();
994
+ ctx.globalAlpha = 1;
995
+ ctx.beginPath();
996
+ ctx.roundRect(304, 187 - mY, 557, 36, [14]);
997
+ ctx.clip();
998
+ const barColors = resolveCardColors({ custom: barColor });
999
+ if (barColors.length > 20)
1000
+ throw new KiraError(
1001
+ `Invalid barColor length (${barColors.length}) must be a maximum of 20 colors`
1002
+ );
1003
+ const barWidth = Math.round(currentXp * 556 / requiredXp);
1004
+ const grd = ctx.createLinearGradient(304, 197, 860, 197);
1005
+ for (let i = 0; i < barColors.length; i++) {
1006
+ const stop = barColors.length === 1 ? 0 : i / (barColors.length - 1);
1007
+ grd.addColorStop(stop, parseHex(barColors[i]));
1008
+ }
1009
+ ctx.fillStyle = barColors.length ? grd : "#fff";
1010
+ ctx.beginPath();
1011
+ ctx.roundRect(304, 187 - mY, barWidth, 36, [14]);
1012
+ ctx.fill();
1013
+ return canvas;
1014
+ }
1015
+ function addShadow(canvasToEdit) {
1016
+ const canvas = createCanvas6(885, 303);
1017
+ const ctx = canvas.getContext("2d");
1018
+ ctx.filter = "drop-shadow(0px 4px 4px #000)";
1019
+ ctx.globalAlpha = alphaValue;
1020
+ ctx.drawImage(canvasToEdit, 0, 0);
1021
+ return canvas;
1022
+ }
1023
+
1024
+ // Src/Utils/output.utils.ts
1025
+ var MIME_MAP = {
1026
+ png: "image/png",
1027
+ jpeg: "image/jpeg",
1028
+ webp: "image/webp"
1029
+ };
1030
+ var DEFAULT_QUALITY = 90;
1031
+ async function encodeCanvas(canvas, output) {
1032
+ const format = output?.format ?? "png";
1033
+ const mime = MIME_MAP[format];
1034
+ if (!mime) {
1035
+ throw new KiraError(
1036
+ `Invalid output format ('${output?.format}'), must be one of: png, jpeg, webp`,
1037
+ "VALIDATION" /* Validation */
1038
+ );
1039
+ }
1040
+ if (format === "png") {
1041
+ return canvas.toBuffer("image/png");
1042
+ }
1043
+ const quality = output?.quality ?? DEFAULT_QUALITY;
1044
+ if (!Number.isFinite(quality) || quality < 1 || quality > 100) {
1045
+ throw new KiraError(
1046
+ `Invalid output quality (${output?.quality}), must be a number between 1 and 100`,
1047
+ "VALIDATION" /* Validation */
1048
+ );
1049
+ }
1050
+ return canvas.toBuffer(mime, quality);
1051
+ }
1052
+ function extensionForFormat(format) {
1053
+ if (format === "jpeg") return "jpg";
1054
+ if (format === "webp") return "webp";
1055
+ return "png";
1056
+ }
1057
+
1058
+ // Src/Utils/profileImage.output.utils.ts
1059
+ ensureFontsRegistered();
1060
+ async function genPng(data, options) {
1061
+ const { basicInfo, assets } = data;
1062
+ const canvas = createCanvas7(885, 303);
1063
+ const ctx = canvas.getContext("2d");
1064
+ const userAvatar = (assets.avatarURL ?? assets.defaultAvatarURL) + "?size=512";
1065
+ const userBanner = assets.bannerURL ? assets.bannerURL + "?size=512" : null;
1066
+ const badges = await getBadges(data, options);
1067
+ if (options?.removeBorder) ctx.roundRect(9, 9, 867, 285, [26]);
1068
+ else ctx.roundRect(0, 0, 885, 303, [34]);
1069
+ ctx.clip();
1070
+ const cardBase = await genBase(options, userAvatar, userBanner);
1071
+ ctx.drawImage(cardBase, 0, 0);
1072
+ const cardFrame = await genFrame(badges, options);
1073
+ ctx.drawImage(cardFrame, 0, 0);
1074
+ if (!options?.removeNameplate && data?.decoration?.nameplate) {
1075
+ const nameplate = await genNameplate(data);
1076
+ ctx.drawImage(nameplate, 0, 0);
1077
+ }
1078
+ const {
1079
+ canvas: cardTextAndAvatar,
1080
+ verifBadgeX,
1081
+ verifBadgeY
1082
+ } = await genTextAndAvatar(data, options, userAvatar);
1083
+ const textAvatarShadow = addShadow(cardTextAndAvatar);
1084
+ ctx.drawImage(textAvatarShadow, 0, 0);
1085
+ ctx.drawImage(cardTextAndAvatar, 0, 0);
1086
+ const usingAutoNitroColors = !options?.removeBorder && typeof options?.borderColor === "undefined" && !options?.disableProfileTheme && Boolean(data?.decoration?.profileColors?.length);
1087
+ if (usingAutoNitroColors && !options?.borderAllign) options.borderAllign = "vertical";
1088
+ const borderColors = resolveCardColors({
1089
+ custom: options?.borderColor,
1090
+ removeBorder: options?.removeBorder,
1091
+ useNitroTheme: !options?.disableProfileTheme,
1092
+ nitroColors: data?.decoration?.profileColors,
1093
+ useRoleColor: options?.useRoleColor,
1094
+ roleColor: data?.decoration?.roleColor
1095
+ });
1096
+ if (borderColors.length > 0) {
1097
+ const border = await genBorder(borderColors, options);
1098
+ ctx.drawImage(border, 0, 0);
1099
+ }
1100
+ if (basicInfo?.bot) {
1101
+ const botVerifBadge = await genBotVerifBadge(data, verifBadgeX, verifBadgeY);
1102
+ const shadowVerifBadge = addShadow(botVerifBadge);
1103
+ ctx.drawImage(shadowVerifBadge, 0, 0);
1104
+ ctx.drawImage(botVerifBadge, 0, 0);
1105
+ }
1106
+ if (!options?.removeBadges) {
1107
+ const cardBadges = await genBadges(badges);
1108
+ const badgesShadow = addShadow(cardBadges);
1109
+ ctx.drawImage(badgesShadow, 0, 0);
1110
+ ctx.drawImage(cardBadges, 0, 0);
1111
+ }
1112
+ if (options?.rankData) {
1113
+ const xpBar = genXpBar(options);
1114
+ ctx.drawImage(xpBar, 0, 0);
1115
+ }
1116
+ if (!options?.removeAvatarFrame && data?.decoration?.avatarFrame) {
1117
+ const avatarFrame = await genAvatarFrame(data, options);
1118
+ ctx.drawImage(avatarFrame, 0, 0);
1119
+ }
1120
+ return encodeCanvas(canvas, options?.output);
1121
+ }
1122
+
1123
+ // Src/Utils/themes.utils.ts
1124
+ var THEMES = {
1125
+ discord: {
1126
+ accentColor: "#5865F2",
1127
+ borderColor: ["#5865F2", "#404EED"],
1128
+ usernameColor: "#FFFFFF",
1129
+ tagColor: "#B5BAC1",
1130
+ messageColor: "#DBDEE1",
1131
+ levelColor: "#5865F2",
1132
+ barColor: ["#5865F2", "#404EED"],
1133
+ glowColor: "#5865F2",
1134
+ heartColor: "#FF4D6D"
1135
+ },
1136
+ midnight: {
1137
+ accentColor: "#7C3AED",
1138
+ borderColor: ["#312E81", "#7C3AED"],
1139
+ usernameColor: "#E0E7FF",
1140
+ tagColor: "#A5B4FC",
1141
+ messageColor: "#C7D2FE",
1142
+ levelColor: "#818CF8",
1143
+ barColor: ["#4338CA", "#7C3AED"],
1144
+ glowColor: "#818CF8",
1145
+ heartColor: "#F472B6"
1146
+ },
1147
+ sunset: {
1148
+ accentColor: "#F97316",
1149
+ borderColor: ["#F97316", "#EC4899"],
1150
+ usernameColor: "#FFF7ED",
1151
+ tagColor: "#FDBA74",
1152
+ messageColor: "#FED7AA",
1153
+ levelColor: "#FB923C",
1154
+ barColor: ["#F97316", "#EC4899"],
1155
+ glowColor: "#FB923C",
1156
+ heartColor: "#EC4899"
1157
+ },
1158
+ neon: {
1159
+ accentColor: "#22D3EE",
1160
+ borderColor: ["#22D3EE", "#D946EF"],
1161
+ usernameColor: "#ECFEFF",
1162
+ tagColor: "#67E8F9",
1163
+ messageColor: "#A5F3FC",
1164
+ levelColor: "#22D3EE",
1165
+ barColor: ["#22D3EE", "#D946EF"],
1166
+ glowColor: "#D946EF",
1167
+ heartColor: "#D946EF"
1168
+ },
1169
+ forest: {
1170
+ accentColor: "#22C55E",
1171
+ borderColor: ["#166534", "#22C55E"],
1172
+ usernameColor: "#F0FDF4",
1173
+ tagColor: "#86EFAC",
1174
+ messageColor: "#BBF7D0",
1175
+ levelColor: "#4ADE80",
1176
+ barColor: ["#166534", "#22C55E"],
1177
+ glowColor: "#4ADE80",
1178
+ heartColor: "#84CC16"
1179
+ },
1180
+ sakura: {
1181
+ accentColor: "#F472B6",
1182
+ borderColor: ["#FBCFE8", "#F472B6"],
1183
+ usernameColor: "#FDF2F8",
1184
+ tagColor: "#F9A8D4",
1185
+ messageColor: "#FBCFE8",
1186
+ levelColor: "#F472B6",
1187
+ barColor: ["#FBCFE8", "#F472B6"],
1188
+ glowColor: "#F9A8D4",
1189
+ heartColor: "#FB7185"
1190
+ },
1191
+ monochrome: {
1192
+ accentColor: "#9CA3AF",
1193
+ borderColor: ["#4B5563", "#D1D5DB"],
1194
+ usernameColor: "#F9FAFB",
1195
+ tagColor: "#9CA3AF",
1196
+ messageColor: "#D1D5DB",
1197
+ levelColor: "#E5E7EB",
1198
+ barColor: ["#4B5563", "#D1D5DB"],
1199
+ glowColor: "#D1D5DB",
1200
+ heartColor: "#9CA3AF"
1201
+ },
1202
+ gold: {
1203
+ accentColor: "#D4AF37",
1204
+ borderColor: ["#3F3F1F", "#D4AF37"],
1205
+ usernameColor: "#FEF9E7",
1206
+ tagColor: "#E9D8A6",
1207
+ messageColor: "#F5E6B8",
1208
+ levelColor: "#D4AF37",
1209
+ barColor: ["#3F3F1F", "#D4AF37"],
1210
+ glowColor: "#D4AF37",
1211
+ heartColor: "#D4AF37"
1212
+ }
1213
+ };
1214
+ function getThemePalette(theme) {
1215
+ if (!theme) return void 0;
1216
+ const palette = THEMES[theme];
1217
+ if (!palette) {
1218
+ throw new KiraError(
1219
+ `Invalid theme ('${theme}'), must be one of: ${Object.keys(THEMES).join(", ")}`,
1220
+ "VALIDATION" /* Validation */
1221
+ );
1222
+ }
1223
+ return palette;
1224
+ }
1225
+
1226
+ // Src/Cards/profileImage.ts
1227
+ async function profileImage(userId, options = {}) {
1228
+ if (!userId || typeof userId !== "string")
1229
+ throw new KiraError(
1230
+ `TypeError: Invalid argument for profileImage()
1231
+ Expected string userId, got ${typeof userId === "undefined" || !userId ? "undefined" : typeof userId}`,
1232
+ "VALIDATION" /* Validation */
1233
+ );
1234
+ const data = await fetchUserData(userId, options.guildId, options.bypassCache);
1235
+ const palette = getThemePalette(options.theme);
1236
+ const resolvedOptions = {
1237
+ ...options,
1238
+ usernameColor: options.usernameColor ?? palette?.usernameColor,
1239
+ tagColor: options.tagColor ?? palette?.tagColor,
1240
+ borderColor: options.borderColor ?? palette?.borderColor
1241
+ };
1242
+ if (resolvedOptions.rankData) {
1243
+ resolvedOptions.rankData = {
1244
+ ...resolvedOptions.rankData,
1245
+ barColor: resolvedOptions.rankData.barColor ?? palette?.barColor,
1246
+ levelColor: resolvedOptions.rankData.levelColor ?? palette?.levelColor
1247
+ };
1248
+ }
1249
+ try {
1250
+ const buffer = await genPng(data, resolvedOptions);
1251
+ return buffer;
1252
+ } catch (error) {
1253
+ if (error instanceof KiraError) throw error;
1254
+ const message = error instanceof Error ? error.message : String(error);
1255
+ if (message.includes("source rejected")) {
1256
+ throw new KiraError(`Error loading user assets, try again later`, "ASSET_LOAD" /* AssetLoad */);
1257
+ }
1258
+ throw new KiraError(message, "RENDER" /* Render */);
1259
+ }
1260
+ }
1261
+
1262
+ // Src/Utils/LeaderboardImage/constants.ts
1263
+ var CARD_WIDTH = 720;
1264
+ var ROW_HEIGHT = 100;
1265
+ var ROW_GAP = 14;
1266
+ var PADDING = 26;
1267
+ var CORNER_RADIUS = 28;
1268
+ var RANK_COLORS = {
1269
+ 1: "#F1C40F",
1270
+ 2: "#B9BBC6",
1271
+ 3: "#D08A3E"
1272
+ };
1273
+ var DEFAULT_ACCENT = "#2DD4BF";
1274
+
1275
+ // Src/Utils/LeaderboardImage/colorUtils.ts
1276
+ function hexToRgb2(hex) {
1277
+ const normalized = parseHex(hex).replace("#", "");
1278
+ const full = normalized.length === 3 ? normalized.split("").map((c) => c + c).join("") : normalized;
1279
+ return {
1280
+ r: parseInt(full.slice(0, 2), 16),
1281
+ g: parseInt(full.slice(2, 4), 16),
1282
+ b: parseInt(full.slice(4, 6), 16)
1283
+ };
1284
+ }
1285
+ function hexToRgba2(hex, alpha) {
1286
+ const { r, g, b } = hexToRgb2(hex);
1287
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
1288
+ }
1289
+
1290
+ // Src/Utils/LeaderboardImage/dimensions.ts
1291
+ import { createCanvas as createCanvas8 } from "@napi-rs/canvas";
1292
+ function getHeaderHeight(hasTitle, hasSubtitle) {
1293
+ if (!hasTitle) return 0;
1294
+ return hasSubtitle ? 96 : 68;
1295
+ }
1296
+ function getCardHeight(rowCount, hasTitle, hasSubtitle) {
1297
+ const header = getHeaderHeight(hasTitle, hasSubtitle);
1298
+ return PADDING * 2 + header + rowCount * ROW_HEIGHT + Math.max(0, rowCount - 1) * ROW_GAP;
1299
+ }
1300
+ function createLeaderboardCanvas(height) {
1301
+ const canvas = createCanvas8(CARD_WIDTH, height);
1302
+ const ctx = canvas.getContext("2d");
1303
+ return { canvas, ctx };
1304
+ }
1305
+
1306
+ // Src/Utils/LeaderboardImage/background.ts
1307
+ import { loadImage as loadImage6 } from "@napi-rs/canvas";
1308
+ async function drawCardBackground(ctx, width, height, backgroundImage, accentColor = DEFAULT_ACCENT) {
1309
+ ctx.beginPath();
1310
+ ctx.roundRect(0, 0, width, height, [CORNER_RADIUS]);
1311
+ ctx.clip();
1312
+ ctx.fillStyle = "#111318";
1313
+ ctx.fillRect(0, 0, width, height);
1314
+ const image = backgroundImage ? await loadImage6(backgroundImage).catch(() => void 0) : void 0;
1315
+ if (image) {
1316
+ const scale = Math.max(width / image.width, height / image.height);
1317
+ const drawW = image.width * scale;
1318
+ const drawH = image.height * scale;
1319
+ const dx = (width - drawW) / 2;
1320
+ const dy = (height - drawH) / 2;
1321
+ ctx.filter = "blur(6px) brightness(55%)";
1322
+ ctx.drawImage(image, dx, dy, drawW, drawH);
1323
+ ctx.filter = "none";
1324
+ ctx.fillStyle = "rgba(10, 11, 14, 0.55)";
1325
+ ctx.fillRect(0, 0, width, height);
1326
+ } else {
1327
+ const glowTL = ctx.createRadialGradient(0, 0, 0, 0, 0, width * 0.75);
1328
+ glowTL.addColorStop(0, hexToRgba2(accentColor, 0.16));
1329
+ glowTL.addColorStop(1, "rgba(0, 0, 0, 0)");
1330
+ ctx.fillStyle = glowTL;
1331
+ ctx.fillRect(0, 0, width, height);
1332
+ const glowBR = ctx.createRadialGradient(width, height, 0, width, height, width * 0.6);
1333
+ glowBR.addColorStop(0, hexToRgba2(accentColor, 0.1));
1334
+ glowBR.addColorStop(1, "rgba(0, 0, 0, 0)");
1335
+ ctx.fillStyle = glowBR;
1336
+ ctx.fillRect(0, 0, width, height);
1337
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.035)";
1338
+ ctx.lineWidth = 1;
1339
+ const step = 34;
1340
+ for (let d = -height; d < width; d += step) {
1341
+ ctx.beginPath();
1342
+ ctx.moveTo(d, height);
1343
+ ctx.lineTo(d + height, 0);
1344
+ ctx.stroke();
1345
+ }
1346
+ }
1347
+ const gradient = ctx.createLinearGradient(0, 0, 0, height);
1348
+ gradient.addColorStop(0, "rgba(0, 0, 0, 0)");
1349
+ gradient.addColorStop(1, "rgba(0, 0, 0, 0.35)");
1350
+ ctx.fillStyle = gradient;
1351
+ ctx.fillRect(0, 0, width, height);
1352
+ }
1353
+ function drawCardBorder(ctx, width, height, colors) {
1354
+ const thickness = 3;
1355
+ ctx.save();
1356
+ const grd = ctx.createLinearGradient(0, 0, width, height);
1357
+ for (let i = 0; i < colors.length; i++) {
1358
+ const stop = colors.length === 1 ? 0 : i / (colors.length - 1);
1359
+ grd.addColorStop(stop, parseHex(colors[i]));
1360
+ }
1361
+ ctx.strokeStyle = grd;
1362
+ ctx.shadowColor = hexToRgba2(parseHex(colors[0]), 0.55);
1363
+ ctx.shadowBlur = 18;
1364
+ ctx.lineWidth = thickness;
1365
+ ctx.beginPath();
1366
+ ctx.roundRect(thickness / 2, thickness / 2, width - thickness, height - thickness, [
1367
+ CORNER_RADIUS
1368
+ ]);
1369
+ ctx.stroke();
1370
+ ctx.restore();
1371
+ }
1372
+
1373
+ // Src/Utils/LeaderboardImage/header.ts
1374
+ function drawTrophyIcon(ctx, x, y, size, color) {
1375
+ const s = size / 24;
1376
+ ctx.save();
1377
+ ctx.translate(x, y);
1378
+ ctx.fillStyle = color;
1379
+ ctx.strokeStyle = color;
1380
+ ctx.lineWidth = 2.1 * s;
1381
+ ctx.lineCap = "round";
1382
+ ctx.beginPath();
1383
+ ctx.arc(4 * s, 6.5 * s, 3 * s, Math.PI * 0.15, Math.PI * 1.85, false);
1384
+ ctx.stroke();
1385
+ ctx.beginPath();
1386
+ ctx.arc(20 * s, 6.5 * s, 3 * s, Math.PI * 1.15, Math.PI * 0.85, true);
1387
+ ctx.stroke();
1388
+ ctx.beginPath();
1389
+ ctx.moveTo(7 * s, 3 * s);
1390
+ ctx.lineTo(17 * s, 3 * s);
1391
+ ctx.lineTo(17 * s, 8 * s);
1392
+ ctx.quadraticCurveTo(17 * s, 14.5 * s, 12 * s, 14.5 * s);
1393
+ ctx.quadraticCurveTo(7 * s, 14.5 * s, 7 * s, 8 * s);
1394
+ ctx.closePath();
1395
+ ctx.fill();
1396
+ ctx.fillRect(10.5 * s, 14.5 * s, 3 * s, 3.5 * s);
1397
+ ctx.beginPath();
1398
+ ctx.roundRect(6 * s, 18 * s, 12 * s, 3 * s, [1.5 * s]);
1399
+ ctx.fill();
1400
+ ctx.restore();
1401
+ }
1402
+ function drawHeader(ctx, title, subtitle, accentColor) {
1403
+ const iconSize = 26;
1404
+ const iconY = PADDING + 2;
1405
+ drawTrophyIcon(ctx, PADDING, iconY, iconSize, accentColor);
1406
+ ctx.font = withFallback("30px Helvetica Bold");
1407
+ ctx.textAlign = "left";
1408
+ ctx.fillStyle = "#FFFFFF";
1409
+ ctx.fillText(title, PADDING + iconSize + 14, PADDING + 30);
1410
+ if (subtitle) {
1411
+ ctx.font = withFallback("18px Helvetica");
1412
+ ctx.fillStyle = hexToRgba2(accentColor, 0.85);
1413
+ ctx.fillText(subtitle, PADDING, PADDING + 56);
1414
+ }
1415
+ }
1416
+
1417
+ // Src/Utils/LeaderboardImage/row.ts
1418
+ import { loadImage as loadImage7 } from "@napi-rs/canvas";
1419
+ async function drawRow(ctx, resolved, rowY, options) {
1420
+ const { entry, data } = resolved;
1421
+ const { basicInfo, assets } = data;
1422
+ const rowColor = RANK_COLORS[entry.rank] ?? (options?.accentColor ? parseHex(options.accentColor) : DEFAULT_ACCENT);
1423
+ const rowWidth = CARD_WIDTH - PADDING * 2;
1424
+ ctx.save();
1425
+ ctx.shadowColor = "rgba(0, 0, 0, 0.35)";
1426
+ ctx.shadowBlur = 16;
1427
+ ctx.shadowOffsetY = 6;
1428
+ ctx.fillStyle = "rgba(18, 20, 26, 0.55)";
1429
+ ctx.beginPath();
1430
+ ctx.roundRect(PADDING, rowY, rowWidth, ROW_HEIGHT, [20]);
1431
+ ctx.fill();
1432
+ ctx.restore();
1433
+ const rowGradient = ctx.createLinearGradient(0, rowY, 0, rowY + ROW_HEIGHT);
1434
+ rowGradient.addColorStop(0, hexToRgba2(rowColor, 0.14));
1435
+ rowGradient.addColorStop(1, hexToRgba2(rowColor, 0.05));
1436
+ ctx.fillStyle = rowGradient;
1437
+ ctx.beginPath();
1438
+ ctx.roundRect(PADDING, rowY, rowWidth, ROW_HEIGHT, [20]);
1439
+ ctx.fill();
1440
+ ctx.strokeStyle = hexToRgba2(rowColor, 0.45);
1441
+ ctx.lineWidth = 1.25;
1442
+ ctx.beginPath();
1443
+ ctx.roundRect(PADDING + 0.75, rowY + 0.75, rowWidth - 1.5, ROW_HEIGHT - 1.5, [20]);
1444
+ ctx.stroke();
1445
+ const circleSize = 60;
1446
+ const circleY = rowY + (ROW_HEIGHT - circleSize) / 2;
1447
+ const rankX = PADDING + 22 + circleSize / 2;
1448
+ ctx.beginPath();
1449
+ ctx.arc(rankX, circleY + circleSize / 2, circleSize / 2, 0, Math.PI * 2);
1450
+ ctx.fillStyle = entry.rank <= 3 ? rowColor : hexToRgba2(rowColor, 0.16);
1451
+ ctx.fill();
1452
+ if (entry.rank > 3) {
1453
+ ctx.strokeStyle = hexToRgba2(rowColor, 0.8);
1454
+ ctx.lineWidth = 2;
1455
+ ctx.beginPath();
1456
+ ctx.arc(rankX, circleY + circleSize / 2, circleSize / 2 - 1, 0, Math.PI * 2);
1457
+ ctx.stroke();
1458
+ }
1459
+ ctx.font = withFallback("bold 24px Helvetica Bold");
1460
+ ctx.textAlign = "center";
1461
+ ctx.fillStyle = entry.rank <= 3 ? "#14151a" : "#f1f2f4";
1462
+ ctx.fillText(`${entry.rank}`, rankX, circleY + circleSize / 2 + 8);
1463
+ const avatarX = PADDING + 22 + circleSize + 20;
1464
+ const avatarUrl = assets.avatarURL ?? assets.defaultAvatarURL;
1465
+ const avatarCx = avatarX + circleSize / 2;
1466
+ const avatarCy = circleY + circleSize / 2;
1467
+ if (entry.rank === 1) {
1468
+ const glow = ctx.createRadialGradient(
1469
+ avatarCx,
1470
+ avatarCy,
1471
+ circleSize / 2,
1472
+ avatarCx,
1473
+ avatarCy,
1474
+ circleSize
1475
+ );
1476
+ glow.addColorStop(0, hexToRgba2(rowColor, 0.35));
1477
+ glow.addColorStop(1, "rgba(0, 0, 0, 0)");
1478
+ ctx.fillStyle = glow;
1479
+ ctx.beginPath();
1480
+ ctx.arc(avatarCx, avatarCy, circleSize, 0, Math.PI * 2);
1481
+ ctx.fill();
1482
+ }
1483
+ ctx.beginPath();
1484
+ ctx.arc(avatarCx, avatarCy, circleSize / 2 + 3, 0, Math.PI * 2);
1485
+ ctx.strokeStyle = rowColor;
1486
+ ctx.lineWidth = 3;
1487
+ ctx.stroke();
1488
+ ctx.beginPath();
1489
+ ctx.arc(avatarCx, avatarCy, circleSize / 2, 0, Math.PI * 2);
1490
+ ctx.fillStyle = "#292b2f";
1491
+ ctx.fill();
1492
+ const avatarImage = await loadImage7(avatarUrl).catch(() => void 0);
1493
+ if (avatarImage) {
1494
+ ctx.save();
1495
+ ctx.beginPath();
1496
+ ctx.arc(avatarCx, avatarCy, circleSize / 2, 0, Math.PI * 2);
1497
+ ctx.clip();
1498
+ ctx.drawImage(avatarImage, avatarX, circleY, circleSize, circleSize);
1499
+ ctx.restore();
1500
+ }
1501
+ const levelText = `Nv. ${abbreviateNumber(entry.level)}`;
1502
+ ctx.font = withFallback("bold 17px Helvetica Bold");
1503
+ const levelTextWidth = ctx.measureText(levelText).width;
1504
+ const pillPaddingX = 14;
1505
+ const pillWidth = levelTextWidth + pillPaddingX * 2;
1506
+ const pillHeight = 30;
1507
+ const pillX = CARD_WIDTH - PADDING - 16 - pillWidth;
1508
+ const pillY = rowY + 20;
1509
+ ctx.fillStyle = hexToRgba2(rowColor, 0.16);
1510
+ ctx.beginPath();
1511
+ ctx.roundRect(pillX, pillY, pillWidth, pillHeight, [pillHeight / 2]);
1512
+ ctx.fill();
1513
+ ctx.strokeStyle = hexToRgba2(rowColor, 0.7);
1514
+ ctx.lineWidth = 1.5;
1515
+ ctx.beginPath();
1516
+ ctx.roundRect(pillX, pillY, pillWidth, pillHeight, [pillHeight / 2]);
1517
+ ctx.stroke();
1518
+ ctx.font = withFallback("bold 17px Helvetica Bold");
1519
+ ctx.textAlign = "center";
1520
+ ctx.fillStyle = options?.levelColor ? parseHex(options.levelColor) : "#f1f2f4";
1521
+ ctx.fillText(levelText, pillX + pillWidth / 2, pillY + pillHeight / 2 + 6);
1522
+ const textX = avatarX + circleSize + 22;
1523
+ const displayName = truncateText(basicInfo.globalName || basicInfo.username, 22);
1524
+ const maxNameWidth = pillX - 16 - textX;
1525
+ ctx.font = withFallback("21px Helvetica Bold");
1526
+ ctx.textAlign = "left";
1527
+ ctx.fillStyle = options?.usernameColor ? parseHex(options.usernameColor) : "#FFFFFF";
1528
+ ctx.fillText(displayName, textX, rowY + 34, Math.max(maxNameWidth, 40));
1529
+ const barX = textX;
1530
+ const barWidth = CARD_WIDTH - PADDING - 16 - barX;
1531
+ const barY = rowY + 56;
1532
+ const barHeight = 16;
1533
+ ctx.fillStyle = "rgba(0, 0, 0, 0.35)";
1534
+ ctx.beginPath();
1535
+ ctx.roundRect(barX, barY, barWidth, barHeight, [barHeight / 2]);
1536
+ ctx.fill();
1537
+ const progress = entry.requiredXp > 0 ? Math.min(entry.currentXp / entry.requiredXp, 1) : 0;
1538
+ const minVisibleWidth = Math.min(barWidth, barHeight * 2.5);
1539
+ const filledWidth = progress > 0 ? Math.max(minVisibleWidth, Math.round(barWidth * progress)) : 0;
1540
+ const barColors = resolveCardColors({
1541
+ custom: options?.barColor,
1542
+ fallback: [rowColor, hexToRgba2(rowColor, 0.6)]
1543
+ });
1544
+ const grd = ctx.createLinearGradient(barX, 0, barX + filledWidth, 0);
1545
+ for (let i = 0; i < barColors.length; i++) {
1546
+ const stop = barColors.length === 1 ? 0 : i / (barColors.length - 1);
1547
+ grd.addColorStop(stop, barColors[i].startsWith("rgba") ? barColors[i] : parseHex(barColors[i]));
1548
+ }
1549
+ if (progress > 0) {
1550
+ ctx.save();
1551
+ ctx.fillStyle = grd;
1552
+ ctx.beginPath();
1553
+ ctx.roundRect(barX, barY, filledWidth, barHeight, [barHeight / 2]);
1554
+ ctx.fill();
1555
+ ctx.fillStyle = "rgba(255, 255, 255, 0.35)";
1556
+ ctx.beginPath();
1557
+ ctx.roundRect(barX + 3, barY + 2, Math.max(0, filledWidth - 6), barHeight * 0.28, [
1558
+ barHeight / 2
1559
+ ]);
1560
+ ctx.fill();
1561
+ ctx.restore();
1562
+ }
1563
+ ctx.font = withFallback("bold 13px Helvetica");
1564
+ ctx.textAlign = "right";
1565
+ ctx.fillStyle = hexToRgba2("#f1f2f4", 0.85);
1566
+ ctx.fillText(
1567
+ `${abbreviateNumber(entry.currentXp)} / ${abbreviateNumber(entry.requiredXp)} XP`,
1568
+ barX + barWidth,
1569
+ barY + barHeight + 16
1570
+ );
1571
+ }
1572
+
1573
+ // Src/Utils/LeaderboardImage/index.ts
1574
+ ensureFontsRegistered();
1575
+
1576
+ // Src/Utils/leaderboardImage.output.utils.ts
1577
+ ensureFontsRegistered();
1578
+ async function genLeaderboardPng(resolvedEntries, options, borderColors) {
1579
+ const hasTitle = Boolean(options?.title);
1580
+ const hasSubtitle = Boolean(options?.subtitle);
1581
+ const height = getCardHeight(resolvedEntries.length, hasTitle, hasSubtitle);
1582
+ const { canvas, ctx } = createLeaderboardCanvas(height);
1583
+ const accentColor = options?.accentColor ? parseHex(options.accentColor) : DEFAULT_ACCENT;
1584
+ await drawCardBackground(ctx, CARD_WIDTH, height, options?.backgroundImage, accentColor);
1585
+ if (borderColors.length > 0) {
1586
+ drawCardBorder(ctx, CARD_WIDTH, height, borderColors);
1587
+ }
1588
+ if (hasTitle) {
1589
+ drawHeader(ctx, options.title, options.subtitle, accentColor);
1590
+ }
1591
+ const startY = PADDING + getHeaderHeight(hasTitle, hasSubtitle);
1592
+ for (let i = 0; i < resolvedEntries.length; i++) {
1593
+ const rowY = startY + i * (ROW_HEIGHT + ROW_GAP);
1594
+ await drawRow(ctx, resolvedEntries[i], rowY, options);
1595
+ }
1596
+ return encodeCanvas(canvas, options?.output);
1597
+ }
1598
+
1599
+ // Src/Cards/leaderboardImage.ts
1600
+ var DEFAULT_MAX_ENTRIES = 5;
1601
+ var HARD_MAX_ENTRIES = 15;
1602
+ async function leaderboardImage(entries, options = {}) {
1603
+ if (!Array.isArray(entries) || entries.length === 0) {
1604
+ throw new KiraError(
1605
+ "leaderboardImage() expects a non-empty array of LeaderboardEntry",
1606
+ "VALIDATION" /* Validation */
1607
+ );
1608
+ }
1609
+ const requestedMax = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
1610
+ if (!Number.isFinite(requestedMax) || requestedMax < 1) {
1611
+ throw new KiraError(
1612
+ `Invalid maxEntries (${options.maxEntries}), must be a number between 1 and ${HARD_MAX_ENTRIES}`,
1613
+ "VALIDATION" /* Validation */
1614
+ );
1615
+ }
1616
+ const maxEntries = Math.min(requestedMax, HARD_MAX_ENTRIES);
1617
+ const topEntries = entries.slice(0, maxEntries);
1618
+ for (const entry of topEntries) {
1619
+ if (!entry?.userId || typeof entry.userId !== "string") {
1620
+ throw new KiraError(
1621
+ "Every leaderboard entry requires a valid userId",
1622
+ "VALIDATION" /* Validation */
1623
+ );
1624
+ }
1625
+ if (typeof entry.currentXp !== "number" || typeof entry.requiredXp !== "number" || typeof entry.level !== "number" || !Number.isFinite(entry.currentXp) || !Number.isFinite(entry.requiredXp) || !Number.isFinite(entry.level)) {
1626
+ throw new KiraError(
1627
+ `Invalid leaderboard entry for userId (${entry.userId}): currentXp, requiredXp and level are required numbers`,
1628
+ "VALIDATION" /* Validation */
1629
+ );
1630
+ }
1631
+ }
1632
+ const resolvedEntries = await Promise.all(
1633
+ topEntries.map(async (entry, index) => {
1634
+ const data = await fetchUserData(entry.userId, options.guildId, options.bypassCache);
1635
+ return {
1636
+ entry: { ...entry, rank: entry.rank ?? index + 1 },
1637
+ data
1638
+ };
1639
+ })
1640
+ );
1641
+ try {
1642
+ const palette = getThemePalette(options.theme);
1643
+ const accentColor = options.accentColor ?? palette?.accentColor ?? DEFAULT_ACCENT;
1644
+ const topEntryData = resolvedEntries[0]?.data;
1645
+ const borderColors = resolveCardColors({
1646
+ custom: options.borderColor,
1647
+ removeBorder: options.removeBorder,
1648
+ useNitroTheme: options.useNitroTheme,
1649
+ nitroColors: topEntryData?.decoration.profileColors,
1650
+ useRoleColor: options.useRoleColor,
1651
+ roleColor: topEntryData?.decoration.roleColor,
1652
+ fallback: palette?.borderColor ?? [accentColor]
1653
+ });
1654
+ const resolvedOptions = {
1655
+ ...options,
1656
+ accentColor,
1657
+ usernameColor: options.usernameColor ?? palette?.usernameColor,
1658
+ levelColor: options.levelColor ?? palette?.levelColor,
1659
+ barColor: options.barColor ?? palette?.barColor
1660
+ };
1661
+ return await genLeaderboardPng(resolvedEntries, resolvedOptions, borderColors);
1662
+ } catch (error) {
1663
+ if (error instanceof KiraError) throw error;
1664
+ const message = error instanceof Error ? error.message : String(error);
1665
+ throw new KiraError(message, "RENDER" /* Render */);
1666
+ }
1667
+ }
1668
+
1669
+ // Src/Utils/MemberEeventCard/constants.ts
1670
+ var CARD_WIDTH2 = 700;
1671
+ var CORNER_RADIUS2 = 28;
1672
+ var BANNER_HEIGHT = 170;
1673
+ var BASE_AVATAR_SIZE = 172;
1674
+ var AVATAR_BORDER = 6;
1675
+
1676
+ // Src/Utils/MemberEeventCard/dimensions.ts
1677
+ import { createCanvas as createCanvas9 } from "@napi-rs/canvas";
1678
+ function normalizeFontScale(fontScale) {
1679
+ return fontScale && fontScale > 0 ? fontScale : 1;
1680
+ }
1681
+ function computeAvatarSize(fontScale) {
1682
+ const scale = 1 + (fontScale - 1) * 0.3;
1683
+ return Math.round(BASE_AVATAR_SIZE * scale);
1684
+ }
1685
+ function computeBodyHeight(hasMessage, hasMemberCount, hasDate, fontScale) {
1686
+ let h = 40 * fontScale;
1687
+ h += 38 * fontScale;
1688
+ if (hasMessage) h += 30 * fontScale;
1689
+ if (hasMemberCount) h += 28 * fontScale;
1690
+ if (hasDate) h += 24 * fontScale;
1691
+ h += 28;
1692
+ return h;
1693
+ }
1694
+ function getCardDimensions(layout) {
1695
+ const hasMessage = Boolean(layout.message);
1696
+ const hasMemberCount = layout.memberCount != null;
1697
+ const hasDate = Boolean(layout.dateText);
1698
+ const fontScale = normalizeFontScale(layout.fontScale);
1699
+ const avatarSize = computeAvatarSize(fontScale);
1700
+ const bodyHeight = computeBodyHeight(hasMessage, hasMemberCount, hasDate, fontScale);
1701
+ const height = Math.round(BANNER_HEIGHT + avatarSize / 2 + bodyHeight);
1702
+ const baseAvatarSize = computeAvatarSize(1);
1703
+ const baseBodyHeight = computeBodyHeight(hasMessage, hasMemberCount, hasDate, 1);
1704
+ const baseHeight = BANNER_HEIGHT + baseAvatarSize / 2 + baseBodyHeight;
1705
+ const aspectRatio = CARD_WIDTH2 / baseHeight;
1706
+ const width = Math.round(height * aspectRatio);
1707
+ return { width, height };
1708
+ }
1709
+ function createMemberEventCanvas(width, height) {
1710
+ const canvas = createCanvas9(width, height);
1711
+ const ctx = canvas.getContext("2d");
1712
+ return { canvas, ctx };
1713
+ }
1714
+
1715
+ // Src/Utils/MemberEeventCard/render.ts
1716
+ import { loadImage as loadImage9 } from "@napi-rs/canvas";
1717
+
1718
+ // Src/Utils/MemberEeventCard/textEffects.ts
1719
+ function applyTextEffect(ctx, effect, glowColor) {
1720
+ if (effect === "shadow") {
1721
+ ctx.shadowColor = "rgba(0, 0, 0, 0.65)";
1722
+ ctx.shadowBlur = 6;
1723
+ ctx.shadowOffsetX = 0;
1724
+ ctx.shadowOffsetY = 3;
1725
+ } else if (effect === "glow") {
1726
+ ctx.shadowColor = glowColor;
1727
+ ctx.shadowBlur = 20;
1728
+ ctx.shadowOffsetX = 0;
1729
+ ctx.shadowOffsetY = 0;
1730
+ } else {
1731
+ ctx.shadowColor = "transparent";
1732
+ ctx.shadowBlur = 0;
1733
+ ctx.shadowOffsetX = 0;
1734
+ ctx.shadowOffsetY = 0;
1735
+ }
1736
+ }
1737
+
1738
+ // Src/Utils/MemberEeventCard/pills.ts
1739
+ import { loadImage as loadImage8 } from "@napi-rs/canvas";
1740
+ async function drawServerTagPill(ctx, serverTag) {
1741
+ const text = truncateText(serverTag.text, 12);
1742
+ const iconSize = serverTag.badgeURL ? 18 : 0;
1743
+ const paddingX = 10;
1744
+ const pillHeight = 26;
1745
+ const gap = iconSize ? 6 : 0;
1746
+ ctx.font = withFallback("13px Helvetica Bold");
1747
+ const textWidth = ctx.measureText(text).width;
1748
+ const pillWidth = paddingX * 2 + iconSize + gap + textWidth;
1749
+ const pillX = 16;
1750
+ const pillY = 16;
1751
+ ctx.save();
1752
+ ctx.beginPath();
1753
+ ctx.roundRect(pillX, pillY, pillWidth, pillHeight, [pillHeight / 2]);
1754
+ ctx.fillStyle = "rgba(10, 10, 14, 0.55)";
1755
+ ctx.fill();
1756
+ ctx.restore();
1757
+ let cursorX = pillX + paddingX;
1758
+ if (iconSize && serverTag.badgeURL) {
1759
+ const badgeImage = await loadImage8(serverTag.badgeURL).catch((_error) => {
1760
+ return void 0;
1761
+ });
1762
+ if (badgeImage) {
1763
+ ctx.drawImage(badgeImage, cursorX, pillY + (pillHeight - iconSize) / 2, iconSize, iconSize);
1764
+ cursorX += iconSize + gap;
1765
+ }
1766
+ }
1767
+ ctx.textAlign = "left";
1768
+ ctx.fillStyle = "#ffffff";
1769
+ ctx.font = withFallback("13px Helvetica Bold");
1770
+ ctx.fillText(text, cursorX, pillY + pillHeight / 2 + 4);
1771
+ }
1772
+ async function drawBadgesPill(ctx, width, badges, maxBadges, iconSize = 20, pillHeight = 32) {
1773
+ const shown = badges.slice(0, maxBadges > 0 ? maxBadges : 3);
1774
+ if (!shown.length) return;
1775
+ const gap = Math.round(iconSize * 0.3);
1776
+ const paddingX = Math.round(iconSize * 0.5);
1777
+ const totalIconsWidth = shown.length * iconSize + (shown.length - 1) * gap;
1778
+ const pillWidth = paddingX * 2 + totalIconsWidth;
1779
+ const pillX = width - 16 - pillWidth;
1780
+ const pillY = 16;
1781
+ ctx.save();
1782
+ ctx.beginPath();
1783
+ ctx.roundRect(pillX, pillY, pillWidth, pillHeight, [pillHeight / 2]);
1784
+ ctx.fillStyle = "rgba(10, 10, 14, 0.55)";
1785
+ ctx.fill();
1786
+ ctx.restore();
1787
+ let cursorX = pillX + paddingX;
1788
+ for (const badge of shown) {
1789
+ const badgeImage = await loadImage8(badge.icon).catch(() => void 0);
1790
+ if (badgeImage) {
1791
+ ctx.drawImage(badgeImage, cursorX, pillY + (pillHeight - iconSize) / 2, iconSize, iconSize);
1792
+ }
1793
+ cursorX += iconSize + gap;
1794
+ }
1795
+ }
1796
+
1797
+ // Src/Utils/MemberEeventCard/render.ts
1798
+ async function drawMemberEventCard(ctx, width, height, avatarUrl, username, layout) {
1799
+ const fontScale = normalizeFontScale(layout.fontScale);
1800
+ const avatarSize = computeAvatarSize(fontScale);
1801
+ const avatarBorder = Math.max(4, Math.round(AVATAR_BORDER * (avatarSize / BASE_AVATAR_SIZE)));
1802
+ const avatarBorderColor = layout.avatarBorderColor ? parseHex(layout.avatarBorderColor) : layout.accentColor;
1803
+ ctx.save();
1804
+ ctx.beginPath();
1805
+ ctx.roundRect(0, 0, width, height, [CORNER_RADIUS2]);
1806
+ ctx.clip();
1807
+ ctx.fillStyle = "#111318";
1808
+ ctx.fillRect(0, 0, width, height);
1809
+ const bannerImage = layout.customBackground ? await loadImage9(layout.customBackground).catch(() => void 0) : void 0;
1810
+ if (bannerImage) {
1811
+ drawCoverImage(ctx, bannerImage, 0, 0, width, height);
1812
+ const overlay = ctx.createLinearGradient(0, 0, 0, height);
1813
+ overlay.addColorStop(0, "rgba(8, 9, 12, 0.1)");
1814
+ overlay.addColorStop(0.3, "rgba(8, 9, 12, 0.3)");
1815
+ overlay.addColorStop(0.55, "rgba(13, 15, 20, 0.55)");
1816
+ overlay.addColorStop(1, "rgba(13, 15, 20, 0.9)");
1817
+ ctx.fillStyle = overlay;
1818
+ ctx.fillRect(0, 0, width, height);
1819
+ } else {
1820
+ const grd = ctx.createLinearGradient(0, 0, width, BANNER_HEIGHT);
1821
+ grd.addColorStop(0, hexToRgba(layout.accentColor, 0.85));
1822
+ grd.addColorStop(1, hexToRgba(layout.accentColor, 0.3));
1823
+ ctx.fillStyle = grd;
1824
+ ctx.fillRect(0, 0, width, BANNER_HEIGHT);
1825
+ }
1826
+ if (layout.serverTag) {
1827
+ await drawServerTagPill(ctx, layout.serverTag);
1828
+ }
1829
+ if (layout.badges?.length) {
1830
+ await drawBadgesPill(ctx, width, layout.badges, layout.maxBadges ?? 3);
1831
+ }
1832
+ const avatarCx = width / 2;
1833
+ const avatarCy = BANNER_HEIGHT;
1834
+ const outerRadius = avatarSize / 2 + avatarBorder;
1835
+ ctx.beginPath();
1836
+ ctx.arc(avatarCx, avatarCy, outerRadius, 0, Math.PI * 2);
1837
+ ctx.fillStyle = "#111318";
1838
+ ctx.fill();
1839
+ ctx.beginPath();
1840
+ ctx.arc(avatarCx, avatarCy, outerRadius - avatarBorder / 2, 0, Math.PI * 2);
1841
+ ctx.strokeStyle = avatarBorderColor;
1842
+ ctx.lineWidth = avatarBorder;
1843
+ ctx.stroke();
1844
+ ctx.beginPath();
1845
+ ctx.arc(avatarCx, avatarCy, avatarSize / 2, 0, Math.PI * 2);
1846
+ ctx.fillStyle = "#292b2f";
1847
+ ctx.fill();
1848
+ const avatarImage = await loadImage9(avatarUrl).catch(() => void 0);
1849
+ if (avatarImage) {
1850
+ ctx.save();
1851
+ ctx.beginPath();
1852
+ ctx.arc(avatarCx, avatarCy, avatarSize / 2, 0, Math.PI * 2);
1853
+ ctx.clip();
1854
+ ctx.drawImage(
1855
+ avatarImage,
1856
+ avatarCx - avatarSize / 2,
1857
+ avatarCy - avatarSize / 2,
1858
+ avatarSize,
1859
+ avatarSize
1860
+ );
1861
+ ctx.restore();
1862
+ }
1863
+ const avatarFrameImage = layout.avatarFrameUrl ? await loadImage9(layout.avatarFrameUrl).catch(() => void 0) : void 0;
1864
+ if (avatarFrameImage) {
1865
+ const frameSize = avatarSize * 1.2;
1866
+ ctx.drawImage(
1867
+ avatarFrameImage,
1868
+ avatarCx - frameSize / 2,
1869
+ avatarCy - frameSize / 2,
1870
+ frameSize,
1871
+ frameSize
1872
+ );
1873
+ }
1874
+ let cursorY = BANNER_HEIGHT + avatarSize / 2 + 44 * fontScale;
1875
+ const glowColor = layout.glowColor ? parseHex(layout.glowColor) : layout.accentColor;
1876
+ ctx.textAlign = "center";
1877
+ applyTextEffect(ctx, layout.usernameEffect, glowColor);
1878
+ ctx.font = withFallback(`${Math.round(30 * fontScale)}px Helvetica Bold`);
1879
+ ctx.fillStyle = layout.usernameColor ? parseHex(layout.usernameColor) : "#FFFFFF";
1880
+ ctx.fillText(truncateText(username, 24), width / 2, cursorY);
1881
+ if (layout.message) {
1882
+ cursorY += 32 * fontScale;
1883
+ applyTextEffect(ctx, layout.messageEffect, glowColor);
1884
+ ctx.font = withFallback(`${Math.round(18 * fontScale)}px Helvetica`);
1885
+ ctx.fillStyle = layout.messageColor ? parseHex(layout.messageColor) : "rgba(255, 255, 255, 0.85)";
1886
+ ctx.fillText(truncateText(layout.message, 64), width / 2, cursorY);
1887
+ }
1888
+ applyTextEffect(ctx, "none", glowColor);
1889
+ if (layout.memberCount != null) {
1890
+ cursorY += 30 * fontScale;
1891
+ ctx.font = withFallback(`bold ${Math.round(16 * fontScale)}px Helvetica Bold`);
1892
+ ctx.fillStyle = hexToRgba(layout.accentColor, 1);
1893
+ ctx.fillText(`Miembro #${layout.memberCount}`, width / 2, cursorY);
1894
+ }
1895
+ if (layout.dateText) {
1896
+ cursorY += 26 * fontScale;
1897
+ ctx.font = withFallback(`${Math.round(14 * fontScale)}px Helvetica`);
1898
+ ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
1899
+ ctx.fillText(layout.dateText, width / 2, cursorY);
1900
+ }
1901
+ ctx.restore();
1902
+ if (layout.borderColors.length > 0) {
1903
+ drawGradientBorder(ctx, width, height, layout.borderColors, CORNER_RADIUS2);
1904
+ }
1905
+ }
1906
+
1907
+ // Src/Utils/memberEventCard.output.utils.ts
1908
+ async function genMemberEventPng(avatarUrl, username, layout, output) {
1909
+ const { width, height } = getCardDimensions(layout);
1910
+ const { canvas, ctx } = createMemberEventCanvas(width, height);
1911
+ await drawMemberEventCard(ctx, width, height, avatarUrl, username, layout);
1912
+ return encodeCanvas(canvas, output);
1913
+ }
1914
+
1915
+ // Src/Cards/memberEvent.ts
1916
+ var DEFAULT_ACCENT2 = "#2DD4BF";
1917
+ function formatMessage(template, username, guildName, memberCount) {
1918
+ return template.replace(/\{username\}/g, username).replace(/\{user\}/g, username).replace(/\{server\}/g, guildName).replace(/\{memberCount\}/g, memberCount != null ? String(memberCount) : "");
1919
+ }
1920
+ async function renderMemberEventCard(userId, guildName, defaultMessage, options = {}) {
1921
+ if (!userId || typeof userId !== "string") {
1922
+ throw new KiraError("A valid userId is required", "VALIDATION" /* Validation */);
1923
+ }
1924
+ if (!guildName || typeof guildName !== "string") {
1925
+ throw new KiraError("A valid guildName is required", "VALIDATION" /* Validation */);
1926
+ }
1927
+ const data = await fetchUserData(userId, options.guildId, options.bypassCache);
1928
+ const username = data.basicInfo.globalName || data.basicInfo.username;
1929
+ const avatarUrl = data.assets.avatarURL ?? data.assets.defaultAvatarURL;
1930
+ const palette = getThemePalette(options.theme);
1931
+ const accentColor = options.accentColor ? parseHex(options.accentColor) : palette?.accentColor ? parseHex(palette.accentColor) : DEFAULT_ACCENT2;
1932
+ const borderColors = resolveCardColors({
1933
+ custom: options.borderColor,
1934
+ removeBorder: options.removeBorder,
1935
+ useNitroTheme: options.useNitroTheme,
1936
+ nitroColors: data.decoration.profileColors,
1937
+ useRoleColor: options.useRoleColor,
1938
+ roleColor: data.decoration.roleColor,
1939
+ fallback: palette?.borderColor ?? [accentColor]
1940
+ });
1941
+ const rawMessage = options.message ?? defaultMessage;
1942
+ const message = formatMessage(rawMessage, username, guildName, options.memberCount);
1943
+ const dateText = options.showDate ? getDateOrString(options.date, Date.now(), options.localDateType ?? "es") : void 0;
1944
+ const fontScale = options.fontScale && options.fontScale > 0 ? options.fontScale : 1;
1945
+ const avatarFrameUrl = options.showAvatarDecoration ? data.decoration.avatarFrame : null;
1946
+ const serverTag = options.showServerTag ? data.decoration.serverTag : null;
1947
+ const badges = options.showBadges ? data.assets.badges : void 0;
1948
+ const layout = {
1949
+ message,
1950
+ memberCount: options.memberCount,
1951
+ dateText,
1952
+ accentColor,
1953
+ borderColors,
1954
+ usernameColor: options.usernameColor ?? palette?.usernameColor,
1955
+ messageColor: options.messageColor ?? palette?.messageColor,
1956
+ customBackground: options.customBackground,
1957
+ fontScale,
1958
+ avatarFrameUrl,
1959
+ avatarBorderColor: options.avatarBorderColor,
1960
+ serverTag,
1961
+ badges,
1962
+ maxBadges: options.maxBadges,
1963
+ usernameEffect: options.usernameEffect,
1964
+ messageEffect: options.messageEffect,
1965
+ glowColor: options.glowColor ?? palette?.glowColor
1966
+ };
1967
+ try {
1968
+ return await genMemberEventPng(avatarUrl, username, layout, options.output);
1969
+ } catch (error) {
1970
+ if (error instanceof KiraError) throw error;
1971
+ const message2 = error instanceof Error ? error.message : String(error);
1972
+ throw new KiraError(message2, "RENDER" /* Render */);
1973
+ }
1974
+ }
1975
+
1976
+ // Src/Cards/welcomeImage.ts
1977
+ var DEFAULT_WELCOME_MESSAGE = "\xA1Welcome to {server}!";
1978
+ async function welcomeImage(userId, guildName, options = {}) {
1979
+ return renderMemberEventCard(userId, guildName, DEFAULT_WELCOME_MESSAGE, options);
1980
+ }
1981
+
1982
+ // Src/Cards/leaveImage.ts
1983
+ var DEFAULT_LEAVE_MESSAGE = "{username} has left {server}.";
1984
+ async function leaveImage(userId, guildName, options = {}) {
1985
+ return renderMemberEventCard(userId, guildName, DEFAULT_LEAVE_MESSAGE, options);
1986
+ }
1987
+
1988
+ // Src/Utils/LevelUpCard/constants.ts
1989
+ var CARD_WIDTH3 = 700;
1990
+ var CARD_HEIGHT = 260;
1991
+ var CORNER_RADIUS3 = 26;
1992
+ var AVATAR_SIZE = 128;
1993
+ var AVATAR_BORDER2 = 5;
1994
+
1995
+ // Src/Utils/LevelUpCard/dimensions.ts
1996
+ import { createCanvas as createCanvas10 } from "@napi-rs/canvas";
1997
+ function getCardDimensions2() {
1998
+ return { width: CARD_WIDTH3, height: CARD_HEIGHT };
1999
+ }
2000
+ function createLevelUpCanvas(width, height) {
2001
+ const canvas = createCanvas10(width, height);
2002
+ const ctx = canvas.getContext("2d");
2003
+ return { canvas, ctx };
2004
+ }
2005
+
2006
+ // Src/Utils/ShipCard/decorations.ts
2007
+ function drawSparkle(ctx, cx, cy, size, opacity, color) {
2008
+ ctx.save();
2009
+ ctx.translate(cx, cy);
2010
+ ctx.fillStyle = hexToRgba(color, opacity);
2011
+ ctx.beginPath();
2012
+ ctx.moveTo(0, -size);
2013
+ ctx.quadraticCurveTo(size * 0.18, -size * 0.18, size, 0);
2014
+ ctx.quadraticCurveTo(size * 0.18, size * 0.18, 0, size);
2015
+ ctx.quadraticCurveTo(-size * 0.18, size * 0.18, -size, 0);
2016
+ ctx.quadraticCurveTo(-size * 0.18, -size * 0.18, 0, -size);
2017
+ ctx.closePath();
2018
+ ctx.fill();
2019
+ ctx.restore();
2020
+ }
2021
+ function drawSparkles(ctx, width, height, color) {
2022
+ const spots = [
2023
+ [width * 0.31, height * 0.28, 5, 0.55],
2024
+ [width * 0.37, height * 0.68, 3.5, 0.4],
2025
+ [width * 0.62, height * 0.24, 4, 0.45],
2026
+ [width * 0.67, height * 0.7, 6, 0.5],
2027
+ [width * 0.5, height * 0.16, 3, 0.35]
2028
+ ];
2029
+ for (const [x, y, size, opacity] of spots) {
2030
+ drawSparkle(ctx, x, y, size, opacity, color);
2031
+ }
2032
+ }
2033
+ function drawConnector(ctx, fromX, toX, y, color) {
2034
+ ctx.save();
2035
+ ctx.strokeStyle = hexToRgba(color, 0.4);
2036
+ ctx.lineWidth = 2;
2037
+ ctx.setLineDash([1, 7]);
2038
+ ctx.lineCap = "round";
2039
+ ctx.beginPath();
2040
+ ctx.moveTo(fromX, y);
2041
+ ctx.lineTo(toX, y);
2042
+ ctx.stroke();
2043
+ ctx.restore();
2044
+ }
2045
+ function drawShipNamePill(ctx, width, shipName, color) {
2046
+ const safeName = shipName.normalize("NFKD").replace(/[^\p{L}\p{N}\s]/gu, "").trim();
2047
+ if (!safeName) return;
2048
+ ctx.save();
2049
+ ctx.font = withFallback("bold 15px Helvetica Bold");
2050
+ const paddingX = 16;
2051
+ const textWidth = ctx.measureText(safeName).width;
2052
+ const pillWidth = textWidth + paddingX * 2;
2053
+ const pillHeight = 30;
2054
+ const x = width - 24 - pillWidth;
2055
+ const y = 18;
2056
+ ctx.beginPath();
2057
+ ctx.roundRect(x, y, pillWidth, pillHeight, [pillHeight / 2]);
2058
+ const grd = ctx.createLinearGradient(x, y, x + pillWidth, y);
2059
+ grd.addColorStop(0, hexToRgba(color, 0.9));
2060
+ grd.addColorStop(1, hexToRgba(color, 0.55));
2061
+ ctx.fillStyle = grd;
2062
+ ctx.fill();
2063
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.35)";
2064
+ ctx.lineWidth = 1;
2065
+ ctx.stroke();
2066
+ ctx.textAlign = "center";
2067
+ ctx.textBaseline = "middle";
2068
+ ctx.fillStyle = "#FFFFFF";
2069
+ ctx.shadowColor = "rgba(0, 0, 0, 0.35)";
2070
+ ctx.shadowBlur = 4;
2071
+ ctx.fillText(safeName, x + pillWidth / 2, y + pillHeight / 2 + 1);
2072
+ ctx.restore();
2073
+ }
2074
+ function drawVignette(ctx, width, height) {
2075
+ const grd = ctx.createRadialGradient(
2076
+ width / 2,
2077
+ height / 2,
2078
+ height * 0.25,
2079
+ width / 2,
2080
+ height / 2,
2081
+ Math.max(width, height) * 0.75
2082
+ );
2083
+ grd.addColorStop(0, "rgba(0, 0, 0, 0)");
2084
+ grd.addColorStop(1, "rgba(0, 0, 0, 0.45)");
2085
+ ctx.fillStyle = grd;
2086
+ ctx.fillRect(0, 0, width, height);
2087
+ }
2088
+
2089
+ // Src/Utils/LevelUpCard/render.ts
2090
+ async function drawLevelUpCard(ctx, width, height, layout) {
2091
+ const accentColor = parseHex(layout.accentColor);
2092
+ const avatarBorderColor = layout.avatarBorderColor ? parseHex(layout.avatarBorderColor) : accentColor;
2093
+ const kickerColor = layout.kickerColor ? parseHex(layout.kickerColor) : "#FFFFFF";
2094
+ const levelColor = layout.levelColor ? parseHex(layout.levelColor) : "#FFFFFF";
2095
+ const usernameColor = layout.usernameColor ? parseHex(layout.usernameColor) : "#FFFFFF";
2096
+ const xpColor = layout.xpColor ? parseHex(layout.xpColor) : "rgba(255, 255, 255, 0.75)";
2097
+ const barBackgroundColor = layout.barBackgroundColor ? parseHex(layout.barBackgroundColor) : "rgba(0, 0, 0, 0.35)";
2098
+ const backgroundTint = layout.backgroundTint !== false;
2099
+ ctx.save();
2100
+ ctx.beginPath();
2101
+ ctx.roundRect(0, 0, width, height, [CORNER_RADIUS3]);
2102
+ ctx.clip();
2103
+ ctx.fillStyle = "#0c0d11";
2104
+ ctx.fillRect(0, 0, width, height);
2105
+ const backgroundImage = await loadImageSafe(layout.customBackground);
2106
+ if (backgroundImage) {
2107
+ drawCoverImage(ctx, backgroundImage, 0, 0, width, height);
2108
+ ctx.fillStyle = "rgba(8, 9, 13, 0.6)";
2109
+ ctx.fillRect(0, 0, width, height);
2110
+ if (backgroundTint) {
2111
+ const duotone = ctx.createLinearGradient(0, 0, width, height);
2112
+ duotone.addColorStop(0, hexToRgba(accentColor, 0.35));
2113
+ duotone.addColorStop(1, "rgba(8, 9, 13, 0.45)");
2114
+ ctx.fillStyle = duotone;
2115
+ ctx.fillRect(0, 0, width, height);
2116
+ }
2117
+ } else if (backgroundTint) {
2118
+ const grd = ctx.createLinearGradient(0, 0, width, height);
2119
+ grd.addColorStop(0, hexToRgba(accentColor, 0.38));
2120
+ grd.addColorStop(0.55, "rgba(18, 15, 24, 1)");
2121
+ grd.addColorStop(1, "rgba(10, 11, 15, 1)");
2122
+ ctx.fillStyle = grd;
2123
+ ctx.fillRect(0, 0, width, height);
2124
+ const bgGlow = ctx.createRadialGradient(
2125
+ width * 0.18,
2126
+ height / 2,
2127
+ 10,
2128
+ width * 0.18,
2129
+ height / 2,
2130
+ width * 0.45
2131
+ );
2132
+ bgGlow.addColorStop(0, hexToRgba(accentColor, 0.4));
2133
+ bgGlow.addColorStop(1, "rgba(0, 0, 0, 0)");
2134
+ ctx.fillStyle = bgGlow;
2135
+ ctx.fillRect(0, 0, width, height);
2136
+ } else {
2137
+ const grd = ctx.createLinearGradient(0, 0, width, height);
2138
+ grd.addColorStop(0, "rgba(30, 30, 34, 1)");
2139
+ grd.addColorStop(1, "rgba(10, 11, 15, 1)");
2140
+ ctx.fillStyle = grd;
2141
+ ctx.fillRect(0, 0, width, height);
2142
+ }
2143
+ drawSparkles(ctx, width, height, accentColor);
2144
+ if (layout.badges?.length) {
2145
+ await drawBadgesPill(ctx, width, layout.badges, layout.maxBadges ?? 3, 30, 46);
2146
+ }
2147
+ const hasMessage = Boolean(layout.message);
2148
+ const hasBar = layout.currentXp != null && layout.requiredXp != null && layout.requiredXp > 0;
2149
+ const avatarCx = 104;
2150
+ const avatarCy = height / 2;
2151
+ const outerRadius = AVATAR_SIZE / 2 + AVATAR_BORDER2;
2152
+ if (backgroundTint) {
2153
+ const avatarGlow = ctx.createRadialGradient(
2154
+ avatarCx,
2155
+ avatarCy,
2156
+ outerRadius * 0.6,
2157
+ avatarCx,
2158
+ avatarCy,
2159
+ outerRadius * 1.8
2160
+ );
2161
+ avatarGlow.addColorStop(0, hexToRgba(avatarBorderColor, 0.35));
2162
+ avatarGlow.addColorStop(1, "rgba(0, 0, 0, 0)");
2163
+ ctx.fillStyle = avatarGlow;
2164
+ ctx.beginPath();
2165
+ ctx.arc(avatarCx, avatarCy, outerRadius * 1.8, 0, Math.PI * 2);
2166
+ ctx.fill();
2167
+ }
2168
+ ctx.beginPath();
2169
+ ctx.arc(avatarCx, avatarCy, outerRadius, 0, Math.PI * 2);
2170
+ ctx.fillStyle = "#111318";
2171
+ ctx.fill();
2172
+ ctx.beginPath();
2173
+ ctx.arc(avatarCx, avatarCy, outerRadius - AVATAR_BORDER2 / 2, 0, Math.PI * 2);
2174
+ ctx.strokeStyle = avatarBorderColor;
2175
+ ctx.lineWidth = AVATAR_BORDER2;
2176
+ ctx.stroke();
2177
+ ctx.beginPath();
2178
+ ctx.arc(avatarCx, avatarCy, AVATAR_SIZE / 2, 0, Math.PI * 2);
2179
+ ctx.fillStyle = "#292b2f";
2180
+ ctx.fill();
2181
+ const avatarImage = await loadImageSafe(layout.avatarUrl);
2182
+ if (avatarImage) {
2183
+ ctx.save();
2184
+ ctx.beginPath();
2185
+ ctx.arc(avatarCx, avatarCy, AVATAR_SIZE / 2, 0, Math.PI * 2);
2186
+ ctx.clip();
2187
+ ctx.drawImage(
2188
+ avatarImage,
2189
+ avatarCx - AVATAR_SIZE / 2,
2190
+ avatarCy - AVATAR_SIZE / 2,
2191
+ AVATAR_SIZE,
2192
+ AVATAR_SIZE
2193
+ );
2194
+ ctx.restore();
2195
+ }
2196
+ const avatarFrameImage = await loadImageSafe(layout.avatarFrameUrl ?? void 0);
2197
+ if (avatarFrameImage) {
2198
+ const frameSize = AVATAR_SIZE * 1.2;
2199
+ ctx.drawImage(
2200
+ avatarFrameImage,
2201
+ avatarCx - frameSize / 2,
2202
+ avatarCy - frameSize / 2,
2203
+ frameSize,
2204
+ frameSize
2205
+ );
2206
+ }
2207
+ const contentX = 196;
2208
+ const contentWidth = width - contentX - 40;
2209
+ const rows = [
2210
+ { kind: "kicker", height: 16, gapBefore: 0 },
2211
+ { kind: "username", height: 38, gapBefore: 10 },
2212
+ { kind: "pill", height: 30, gapBefore: 10 }
2213
+ ];
2214
+ if (hasMessage) rows.push({ kind: "message", height: 18, gapBefore: 14 });
2215
+ if (hasBar) rows.push({ kind: "bar", height: 16, gapBefore: hasMessage ? 20 : 24 });
2216
+ const totalHeight = rows.reduce((sum, row) => sum + row.gapBefore + row.height, 0);
2217
+ let rowTop = avatarCy - totalHeight / 2;
2218
+ ctx.textAlign = "left";
2219
+ for (const row of rows) {
2220
+ rowTop += row.gapBefore;
2221
+ const centerY = rowTop + row.height / 2;
2222
+ if (row.kind === "kicker") {
2223
+ ctx.font = withFallback("bold 14px Helvetica Bold");
2224
+ ctx.fillStyle = hexToRgba(kickerColor, 1);
2225
+ ctx.save();
2226
+ ctx.textBaseline = "middle";
2227
+ ctx.fillText("LEVELED UP", contentX, centerY);
2228
+ ctx.restore();
2229
+ } else if (row.kind === "username") {
2230
+ ctx.font = withFallback("bold 34px Helvetica Bold");
2231
+ ctx.fillStyle = usernameColor;
2232
+ ctx.save();
2233
+ ctx.textBaseline = "middle";
2234
+ ctx.fillText(truncateText(layout.username, 22), contentX, centerY, contentWidth);
2235
+ ctx.restore();
2236
+ } else if (row.kind === "pill") {
2237
+ const levelPillText = `LEVEL ${layout.level}`;
2238
+ ctx.font = withFallback("bold 16px Helvetica Bold");
2239
+ const levelPillTextWidth = ctx.measureText(levelPillText).width;
2240
+ const levelPillPaddingX = 16;
2241
+ const levelPillWidth = levelPillTextWidth + levelPillPaddingX * 2;
2242
+ const pillFill = ctx.createLinearGradient(contentX, rowTop, contentX, rowTop + row.height);
2243
+ pillFill.addColorStop(0, hexToRgba(levelColor, 0.24));
2244
+ pillFill.addColorStop(1, hexToRgba(levelColor, 0.12));
2245
+ ctx.fillStyle = pillFill;
2246
+ ctx.beginPath();
2247
+ ctx.roundRect(contentX, rowTop, levelPillWidth, row.height, [row.height / 2]);
2248
+ ctx.fill();
2249
+ ctx.strokeStyle = hexToRgba(levelColor, 0.8);
2250
+ ctx.lineWidth = 1.5;
2251
+ ctx.beginPath();
2252
+ ctx.roundRect(contentX, rowTop, levelPillWidth, row.height, [row.height / 2]);
2253
+ ctx.stroke();
2254
+ ctx.save();
2255
+ ctx.textBaseline = "middle";
2256
+ ctx.fillStyle = levelColor;
2257
+ ctx.fillText(levelPillText, contentX + levelPillPaddingX, centerY);
2258
+ ctx.restore();
2259
+ } else if (row.kind === "message") {
2260
+ ctx.font = withFallback("15px Helvetica");
2261
+ ctx.fillStyle = layout.messageColor ? parseHex(layout.messageColor) : "rgba(255, 255, 255, 0.65)";
2262
+ ctx.save();
2263
+ ctx.textBaseline = "middle";
2264
+ ctx.fillText(truncateText(layout.message, 58), contentX, centerY, contentWidth);
2265
+ ctx.restore();
2266
+ } else if (row.kind === "bar") {
2267
+ const barX = contentX;
2268
+ const barWidth = contentWidth;
2269
+ const barHeight = row.height;
2270
+ ctx.font = withFallback("bold 12px Helvetica");
2271
+ ctx.textAlign = "right";
2272
+ ctx.fillStyle = xpColor;
2273
+ ctx.fillText(
2274
+ `${abbreviateNumber(layout.currentXp)} / ${abbreviateNumber(layout.requiredXp)} XP`,
2275
+ barX + barWidth,
2276
+ rowTop - 8
2277
+ );
2278
+ ctx.textAlign = "left";
2279
+ ctx.fillStyle = barBackgroundColor;
2280
+ ctx.beginPath();
2281
+ ctx.roundRect(barX, rowTop, barWidth, barHeight, [barHeight / 2]);
2282
+ ctx.fill();
2283
+ const progress = Math.min(1, Math.max(0, layout.currentXp / layout.requiredXp));
2284
+ const minVisibleWidth = Math.min(barWidth, barHeight * 1.5);
2285
+ const filledWidth = progress > 0 ? Math.max(minVisibleWidth, Math.round(barWidth * progress)) : 0;
2286
+ if (filledWidth > 0) {
2287
+ const barGrd = ctx.createLinearGradient(barX, 0, barX + filledWidth, 0);
2288
+ for (let i = 0; i < layout.barColors.length; i++) {
2289
+ const stop = layout.barColors.length === 1 ? 0 : i / (layout.barColors.length - 1);
2290
+ barGrd.addColorStop(stop, layout.barColors[i]);
2291
+ }
2292
+ ctx.save();
2293
+ ctx.shadowColor = hexToRgba(layout.barColors[layout.barColors.length - 1], 0.85);
2294
+ ctx.shadowBlur = 12;
2295
+ ctx.fillStyle = barGrd;
2296
+ ctx.beginPath();
2297
+ ctx.roundRect(barX, rowTop, filledWidth, barHeight, [barHeight / 2]);
2298
+ ctx.fill();
2299
+ ctx.shadowBlur = 0;
2300
+ ctx.fillStyle = "rgba(255, 255, 255, 0.35)";
2301
+ ctx.beginPath();
2302
+ ctx.roundRect(barX + 3, rowTop + 2, Math.max(0, filledWidth - 6), barHeight * 0.32, [
2303
+ barHeight / 2
2304
+ ]);
2305
+ ctx.fill();
2306
+ ctx.restore();
2307
+ }
2308
+ }
2309
+ rowTop += row.height;
2310
+ }
2311
+ ctx.restore();
2312
+ if (layout.borderColors.length > 0) {
2313
+ drawGradientBorder(ctx, width, height, layout.borderColors, CORNER_RADIUS3);
2314
+ }
2315
+ }
2316
+
2317
+ // Src/Utils/levelUpCard.output.utils.ts
2318
+ async function genLevelUpPng(layout, output) {
2319
+ const { width, height } = getCardDimensions2();
2320
+ const { canvas, ctx } = createLevelUpCanvas(width, height);
2321
+ await drawLevelUpCard(ctx, width, height, layout);
2322
+ return encodeCanvas(canvas, output);
2323
+ }
2324
+
2325
+ // Src/Cards/levelUpImage.ts
2326
+ var DEFAULT_ACCENT3 = "#FACC15";
2327
+ async function levelUpImage(userId, level, options = {}) {
2328
+ if (!userId || typeof userId !== "string") {
2329
+ throw new KiraError("A valid userId is required", "VALIDATION" /* Validation */);
2330
+ }
2331
+ if (typeof level !== "number" || isNaN(level)) {
2332
+ throw new KiraError("A valid level is required", "VALIDATION" /* Validation */);
2333
+ }
2334
+ const data = await fetchUserData(userId, options.guildId, options.bypassCache);
2335
+ const username = data.basicInfo.globalName || data.basicInfo.username;
2336
+ const avatarUrl = data.assets.avatarURL ?? data.assets.defaultAvatarURL;
2337
+ const palette = getThemePalette(options.theme);
2338
+ const accentColor = options.accentColor ? parseHex(options.accentColor) : palette?.accentColor ? parseHex(palette.accentColor) : DEFAULT_ACCENT3;
2339
+ const borderColors = resolveCardColors({
2340
+ custom: options.borderColor,
2341
+ removeBorder: options.removeBorder,
2342
+ useNitroTheme: options.useNitroTheme,
2343
+ nitroColors: data.decoration.profileColors,
2344
+ useRoleColor: options.useRoleColor,
2345
+ roleColor: data.decoration.roleColor,
2346
+ fallback: palette?.borderColor ?? [accentColor]
2347
+ });
2348
+ const barColors = resolveCardColors({
2349
+ custom: options.barColor,
2350
+ fallback: palette?.barColor ?? [accentColor]
2351
+ });
2352
+ const showAvatarDecoration = options.showAvatarDecoration !== false;
2353
+ const avatarFrameUrl = showAvatarDecoration ? data.decoration.avatarFrame : null;
2354
+ const badges = options.showBadges ? data.assets.badges : void 0;
2355
+ const layout = {
2356
+ username,
2357
+ avatarUrl,
2358
+ level,
2359
+ currentXp: options.currentXp,
2360
+ requiredXp: options.requiredXp,
2361
+ message: options.message,
2362
+ accentColor,
2363
+ borderColors,
2364
+ barColors,
2365
+ barBackgroundColor: options.barBackgroundColor,
2366
+ levelColor: options.levelColor ?? palette?.levelColor,
2367
+ usernameColor: options.usernameColor ?? palette?.usernameColor,
2368
+ messageColor: options.messageColor ?? palette?.messageColor,
2369
+ kickerColor: options.kickerColor,
2370
+ xpColor: options.xpColor,
2371
+ customBackground: options.customBackground,
2372
+ backgroundTint: options.backgroundTint,
2373
+ avatarBorderColor: options.avatarBorderColor,
2374
+ avatarFrameUrl,
2375
+ badges,
2376
+ maxBadges: options.maxBadges
2377
+ };
2378
+ try {
2379
+ return await genLevelUpPng(layout, options.output);
2380
+ } catch (error) {
2381
+ if (error instanceof KiraError) throw error;
2382
+ const message = error instanceof Error ? error.message : String(error);
2383
+ throw new KiraError(message, "RENDER" /* Render */);
2384
+ }
2385
+ }
2386
+
2387
+ // Src/Utils/achievementCard.utils.ts
2388
+ import { createCanvas as createCanvas11, loadImage as loadImage10 } from "@napi-rs/canvas";
2389
+ var CARD_WIDTH4 = 700;
2390
+ var CARD_HEIGHT2 = 220;
2391
+ var CORNER_RADIUS4 = 24;
2392
+ var ICON_SIZE = 96;
2393
+ var ICON_MARGIN = 32;
2394
+ var RARITY_COLORS = {
2395
+ common: "#9AA3AF",
2396
+ rare: "#3B82F6",
2397
+ epic: "#A855F7",
2398
+ legendary: "#F59E0B"
2399
+ };
2400
+ var RARITY_LABELS = {
2401
+ common: "Common",
2402
+ rare: "Queer",
2403
+ epic: "Epic",
2404
+ legendary: "Legendary"
2405
+ };
2406
+ function resolveAccentColor(layout) {
2407
+ if (layout.accentColor) return parseHex(layout.accentColor);
2408
+ const rarity = layout.rarity ?? "common";
2409
+ return RARITY_COLORS[rarity];
2410
+ }
2411
+ function getCardDimensions3() {
2412
+ return { width: CARD_WIDTH4, height: CARD_HEIGHT2 };
2413
+ }
2414
+ function createAchievementCanvas(width, height) {
2415
+ const canvas = createCanvas11(width, height);
2416
+ const ctx = canvas.getContext("2d");
2417
+ return { canvas, ctx };
2418
+ }
2419
+ async function drawAchievementCard(ctx, width, height, layout) {
2420
+ const accentColor = resolveAccentColor(layout);
2421
+ const borderColors = layout.noBorder ? [] : layout.borderColors?.length ? layout.borderColors : [accentColor];
2422
+ ctx.save();
2423
+ ctx.beginPath();
2424
+ ctx.roundRect(0, 0, width, height, [CORNER_RADIUS4]);
2425
+ ctx.clip();
2426
+ ctx.fillStyle = "#111318";
2427
+ ctx.fillRect(0, 0, width, height);
2428
+ const backgroundImage = layout.customBackground ? await loadImage10(layout.customBackground).catch(() => void 0) : void 0;
2429
+ if (backgroundImage) {
2430
+ drawCoverImage(ctx, backgroundImage, 0, 0, width, height);
2431
+ const overlay = ctx.createLinearGradient(0, 0, width, 0);
2432
+ overlay.addColorStop(0, "rgba(10, 11, 15, 0.9)");
2433
+ overlay.addColorStop(1, "rgba(10, 11, 15, 0.55)");
2434
+ ctx.fillStyle = overlay;
2435
+ ctx.fillRect(0, 0, width, height);
2436
+ } else {
2437
+ const grd = ctx.createLinearGradient(0, 0, width, height);
2438
+ grd.addColorStop(0, hexToRgba(accentColor, 0.22));
2439
+ grd.addColorStop(1, "rgba(17, 19, 24, 1)");
2440
+ ctx.fillStyle = grd;
2441
+ ctx.fillRect(0, 0, width, height);
2442
+ }
2443
+ const iconCx = ICON_MARGIN + ICON_SIZE / 2;
2444
+ const iconCy = height / 2;
2445
+ ctx.beginPath();
2446
+ ctx.arc(iconCx, iconCy, ICON_SIZE / 2 + 6, 0, Math.PI * 2);
2447
+ ctx.fillStyle = hexToRgba(accentColor, 0.18);
2448
+ ctx.fill();
2449
+ ctx.beginPath();
2450
+ ctx.arc(iconCx, iconCy, ICON_SIZE / 2, 0, Math.PI * 2);
2451
+ ctx.strokeStyle = accentColor;
2452
+ ctx.lineWidth = 4;
2453
+ ctx.stroke();
2454
+ const iconImage = layout.iconUrl ? await loadImage10(layout.iconUrl).catch(() => void 0) : void 0;
2455
+ if (iconImage) {
2456
+ ctx.save();
2457
+ ctx.beginPath();
2458
+ ctx.arc(iconCx, iconCy, ICON_SIZE / 2 - 6, 0, Math.PI * 2);
2459
+ ctx.clip();
2460
+ ctx.drawImage(
2461
+ iconImage,
2462
+ iconCx - ICON_SIZE / 2 + 6,
2463
+ iconCy - ICON_SIZE / 2 + 6,
2464
+ ICON_SIZE - 12,
2465
+ ICON_SIZE - 12
2466
+ );
2467
+ ctx.restore();
2468
+ } else {
2469
+ ctx.font = withFallback(`${Math.round(ICON_SIZE * 0.45)}px Helvetica Bold`);
2470
+ ctx.textAlign = "center";
2471
+ ctx.fillStyle = accentColor;
2472
+ ctx.fillText("\u2605", iconCx, iconCy + ICON_SIZE * 0.16);
2473
+ }
2474
+ const textX = ICON_MARGIN * 2 + ICON_SIZE;
2475
+ let cursorY = height / 2 - 30;
2476
+ ctx.textAlign = "left";
2477
+ if (layout.usernameLine) {
2478
+ ctx.font = withFallback("14px Helvetica");
2479
+ ctx.fillStyle = "rgba(255, 255, 255, 0.55)";
2480
+ ctx.fillText(truncateText(layout.usernameLine, 40), textX, cursorY);
2481
+ cursorY += 26;
2482
+ }
2483
+ const rarity = layout.rarity ?? "common";
2484
+ ctx.font = withFallback("12px Helvetica Bold");
2485
+ const rarityLabel = RARITY_LABELS[rarity].toUpperCase();
2486
+ const rarityWidth = ctx.measureText(rarityLabel).width;
2487
+ const rarityPadding = 8;
2488
+ const rarityHeight = 20;
2489
+ ctx.beginPath();
2490
+ ctx.roundRect(textX, cursorY - rarityHeight + 4, rarityWidth + rarityPadding * 2, rarityHeight, [
2491
+ rarityHeight / 2
2492
+ ]);
2493
+ ctx.fillStyle = hexToRgba(accentColor, 0.25);
2494
+ ctx.fill();
2495
+ ctx.fillStyle = accentColor;
2496
+ ctx.fillText(rarityLabel, textX + rarityPadding, cursorY - 6);
2497
+ cursorY += 28;
2498
+ ctx.font = withFallback("26px Helvetica Bold");
2499
+ ctx.fillStyle = "#FFFFFF";
2500
+ ctx.fillText(truncateText(layout.title, 34), textX, cursorY);
2501
+ cursorY += 30;
2502
+ if (layout.description) {
2503
+ ctx.font = withFallback("15px Helvetica");
2504
+ ctx.fillStyle = "rgba(255, 255, 255, 0.75)";
2505
+ ctx.fillText(truncateText(layout.description, 60), textX, cursorY);
2506
+ cursorY += 24;
2507
+ }
2508
+ if (layout.progressText) {
2509
+ ctx.font = withFallback("13px Helvetica");
2510
+ ctx.fillStyle = hexToRgba(accentColor, 1);
2511
+ ctx.fillText(layout.progressText, textX, cursorY);
2512
+ }
2513
+ ctx.restore();
2514
+ if (borderColors.length > 0) {
2515
+ drawGradientBorder(ctx, width, height, borderColors, CORNER_RADIUS4);
2516
+ }
2517
+ }
2518
+
2519
+ // Src/Utils/achievementCard.output.utils.ts
2520
+ async function genAchievementPng(layout, output) {
2521
+ const { width, height } = getCardDimensions3();
2522
+ const { canvas, ctx } = createAchievementCanvas(width, height);
2523
+ await drawAchievementCard(ctx, width, height, layout);
2524
+ return encodeCanvas(canvas, output);
2525
+ }
2526
+
2527
+ // Src/Cards/achievementImage.ts
2528
+ async function achievementImage(userId, title, options = {}) {
2529
+ if (!userId || typeof userId !== "string") {
2530
+ throw new KiraError("A valid userId is required", "VALIDATION" /* Validation */);
2531
+ }
2532
+ if (!title || typeof title !== "string") {
2533
+ throw new KiraError("A valid title is required", "VALIDATION" /* Validation */);
2534
+ }
2535
+ const data = await fetchUserData(userId, options.guildId, options.bypassCache);
2536
+ const username = data.basicInfo.globalName || data.basicInfo.username;
2537
+ const palette = getThemePalette(options.theme);
2538
+ const accentColor = options.accentColor ?? palette?.accentColor;
2539
+ const borderColors = resolveCardColors({
2540
+ custom: options.borderColor,
2541
+ removeBorder: options.removeBorder,
2542
+ useNitroTheme: options.useNitroTheme,
2543
+ nitroColors: data.decoration.profileColors,
2544
+ useRoleColor: options.useRoleColor,
2545
+ roleColor: data.decoration.roleColor,
2546
+ fallback: palette?.borderColor
2547
+ });
2548
+ const layout = {
2549
+ title,
2550
+ description: options.description,
2551
+ iconUrl: options.iconUrl,
2552
+ rarity: options.rarity,
2553
+ accentColor,
2554
+ borderColors: borderColors.length ? borderColors : void 0,
2555
+ noBorder: options.removeBorder,
2556
+ customBackground: options.customBackground,
2557
+ usernameLine: options.showUsername === false ? void 0 : username,
2558
+ progressText: options.progressText
2559
+ };
2560
+ try {
2561
+ return await genAchievementPng(layout, options.output);
2562
+ } catch (error) {
2563
+ if (error instanceof KiraError) throw error;
2564
+ const message = error instanceof Error ? error.message : String(error);
2565
+ throw new KiraError(message, "RENDER" /* Render */);
2566
+ }
2567
+ }
2568
+
2569
+ // Src/Utils/ShipCard/constants.ts
2570
+ var CARD_WIDTH5 = 700;
2571
+ var CARD_HEIGHT3 = 260;
2572
+ var CORNER_RADIUS5 = 26;
2573
+ var AVATAR_SIZE2 = 150;
2574
+ var AVATAR_BORDER3 = 6;
2575
+ var HEART_WIDTH = 140;
2576
+ var HEART_HEIGHT = 150;
2577
+ var DEFAULT_ACCENT4 = "#EC4899";
2578
+ var MESSAGE_POOL = [
2579
+ {
2580
+ min: 90,
2581
+ messages: [
2582
+ "Definitely the cutest couple.",
2583
+ "A perfect match\u2014this is true love.",
2584
+ "Soulmates confirmed."
2585
+ ]
2586
+ },
2587
+ {
2588
+ min: 70,
2589
+ messages: [
2590
+ "There's great chemistry here.",
2591
+ "This looks very promising.",
2592
+ "A strong connection is forming."
2593
+ ]
2594
+ },
2595
+ {
2596
+ min: 40,
2597
+ messages: [
2598
+ "It could work with a little effort.",
2599
+ "It's a close call\u2014anything could happen.",
2600
+ "Not amazing, but there's potential."
2601
+ ]
2602
+ },
2603
+ {
2604
+ min: 0,
2605
+ messages: ["Better off staying friends.", "This needs a lot of work.", "Fate had other plans."]
2606
+ }
2607
+ ];
2608
+
2609
+ // Src/Utils/ShipCard/compatibility.ts
2610
+ function hashPair(a, b) {
2611
+ const combined = [a, b].sort().join(":");
2612
+ let hash = 5381;
2613
+ for (let i = 0; i < combined.length; i++) {
2614
+ hash = hash * 33 ^ combined.charCodeAt(i);
2615
+ }
2616
+ return Math.abs(hash);
2617
+ }
2618
+ function computeCompatibility(leftId, rightId) {
2619
+ return hashPair(leftId, rightId) % 101;
2620
+ }
2621
+ function pickShipMessage(leftId, rightId, percentage) {
2622
+ const bucket = MESSAGE_POOL.find((entry) => percentage >= entry.min) ?? MESSAGE_POOL[MESSAGE_POOL.length - 1];
2623
+ const index = hashPair(leftId, rightId) % bucket.messages.length;
2624
+ return bucket.messages[index];
2625
+ }
2626
+
2627
+ // Src/Utils/ShipCard/canvasSetup.ts
2628
+ import { createCanvas as createCanvas12 } from "@napi-rs/canvas";
2629
+ function getCardDimensions4() {
2630
+ return { width: CARD_WIDTH5, height: CARD_HEIGHT3 };
2631
+ }
2632
+ function createShipCanvas(width, height) {
2633
+ const canvas = createCanvas12(width, height);
2634
+ const ctx = canvas.getContext("2d");
2635
+ return { canvas, ctx };
2636
+ }
2637
+
2638
+ // Src/Utils/ShipCard/heartMeter.ts
2639
+ function traceHeartPath(ctx, cx, topY, width, height) {
2640
+ const halfW = width / 2;
2641
+ const lobeHeight = height * 0.3;
2642
+ const midY = topY + (height + lobeHeight) / 2;
2643
+ const bottomY = topY + height;
2644
+ ctx.moveTo(cx, topY + lobeHeight);
2645
+ ctx.bezierCurveTo(cx, topY, cx - halfW, topY, cx - halfW, topY + lobeHeight);
2646
+ ctx.bezierCurveTo(cx - halfW, midY, cx, midY, cx, bottomY);
2647
+ ctx.bezierCurveTo(cx, midY, cx + halfW, midY, cx + halfW, topY + lobeHeight);
2648
+ ctx.bezierCurveTo(cx + halfW, topY, cx, topY, cx, topY + lobeHeight);
2649
+ }
2650
+ function drawHeartIcon(ctx, cx, cy, size, color) {
2651
+ ctx.save();
2652
+ ctx.beginPath();
2653
+ traceHeartPath(ctx, cx, cy - size / 2, size, size);
2654
+ ctx.closePath();
2655
+ ctx.fillStyle = color;
2656
+ ctx.fill();
2657
+ ctx.restore();
2658
+ }
2659
+ function drawHeartMeter(ctx, cx, cy, width, height, percentage, fillColor) {
2660
+ const topY = cy - height / 2;
2661
+ const bottomY = cy + height / 2;
2662
+ ctx.save();
2663
+ ctx.shadowColor = hexToRgba(fillColor, 0.55);
2664
+ ctx.shadowBlur = 22;
2665
+ ctx.beginPath();
2666
+ traceHeartPath(ctx, cx, topY, width, height);
2667
+ ctx.fillStyle = "rgba(8, 9, 13, 0.55)";
2668
+ ctx.fill();
2669
+ ctx.restore();
2670
+ ctx.save();
2671
+ ctx.beginPath();
2672
+ traceHeartPath(ctx, cx, topY, width, height);
2673
+ ctx.clip();
2674
+ ctx.fillStyle = "rgba(255, 255, 255, 0.06)";
2675
+ ctx.fillRect(cx - width, topY, width * 2, height);
2676
+ const fillHeight = height * (percentage / 100);
2677
+ const startY = bottomY - fillHeight;
2678
+ ctx.beginPath();
2679
+ ctx.moveTo(cx - width, bottomY + 4);
2680
+ ctx.lineTo(cx - width, startY);
2681
+ for (let x = -width; x <= width; x += 6) {
2682
+ const waveY = startY + Math.sin((x + cx) * 0.08) * 4;
2683
+ ctx.lineTo(cx + x, waveY);
2684
+ }
2685
+ ctx.lineTo(cx + width, bottomY + 4);
2686
+ ctx.closePath();
2687
+ const fillGrd = ctx.createLinearGradient(0, startY, 0, bottomY);
2688
+ fillGrd.addColorStop(0, hexToRgba(fillColor, 0.95));
2689
+ fillGrd.addColorStop(1, hexToRgba(fillColor, 0.75));
2690
+ ctx.fillStyle = fillGrd;
2691
+ ctx.fill();
2692
+ ctx.fillStyle = "rgba(255, 255, 255, 0.18)";
2693
+ ctx.fillRect(cx - width, startY, width * 2, Math.max(4, fillHeight * 0.12));
2694
+ ctx.restore();
2695
+ ctx.save();
2696
+ ctx.beginPath();
2697
+ traceHeartPath(ctx, cx, topY, width, height);
2698
+ ctx.clip();
2699
+ const shineGrd = ctx.createRadialGradient(
2700
+ cx - width * 0.28,
2701
+ topY + height * 0.32,
2702
+ 2,
2703
+ cx - width * 0.28,
2704
+ topY + height * 0.32,
2705
+ width * 0.6
2706
+ );
2707
+ shineGrd.addColorStop(0, "rgba(255, 255, 255, 0.28)");
2708
+ shineGrd.addColorStop(1, "rgba(255, 255, 255, 0)");
2709
+ ctx.fillStyle = shineGrd;
2710
+ ctx.fillRect(cx - width, topY, width * 2, height);
2711
+ ctx.restore();
2712
+ ctx.save();
2713
+ ctx.beginPath();
2714
+ traceHeartPath(ctx, cx, topY, width, height);
2715
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.9)";
2716
+ ctx.lineWidth = 3.5;
2717
+ ctx.stroke();
2718
+ ctx.restore();
2719
+ ctx.save();
2720
+ ctx.font = withFallback(`bold ${Math.round(height * 0.19)}px Helvetica Bold`);
2721
+ ctx.textAlign = "center";
2722
+ ctx.textBaseline = "middle";
2723
+ ctx.shadowColor = "rgba(0, 0, 0, 0.45)";
2724
+ ctx.shadowBlur = 8;
2725
+ ctx.shadowOffsetY = 2;
2726
+ ctx.fillStyle = "#FFFFFF";
2727
+ ctx.fillText(`${percentage}%`, cx, cy + height * 0.04);
2728
+ ctx.restore();
2729
+ }
2730
+
2731
+ // Src/Utils/ShipCard/avatar.ts
2732
+ async function drawAvatarCircle(ctx, cx, cy, url, accentColor) {
2733
+ const outerRadius = AVATAR_SIZE2 / 2 + AVATAR_BORDER3;
2734
+ ctx.save();
2735
+ ctx.shadowColor = hexToRgba(accentColor, 0.55);
2736
+ ctx.shadowBlur = 26;
2737
+ ctx.beginPath();
2738
+ ctx.arc(cx, cy, outerRadius, 0, Math.PI * 2);
2739
+ ctx.fillStyle = "#0c0d11";
2740
+ ctx.fill();
2741
+ ctx.restore();
2742
+ const ringGrd = ctx.createLinearGradient(cx, cy - outerRadius, cx, cy + outerRadius);
2743
+ ringGrd.addColorStop(0, hexToRgba(accentColor, 1));
2744
+ ringGrd.addColorStop(1, hexToRgba(accentColor, 0.55));
2745
+ ctx.beginPath();
2746
+ ctx.arc(cx, cy, outerRadius - AVATAR_BORDER3 / 2, 0, Math.PI * 2);
2747
+ ctx.strokeStyle = ringGrd;
2748
+ ctx.lineWidth = AVATAR_BORDER3;
2749
+ ctx.stroke();
2750
+ ctx.beginPath();
2751
+ ctx.arc(cx, cy, outerRadius + 2, 0, Math.PI * 2);
2752
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.35)";
2753
+ ctx.lineWidth = 1.5;
2754
+ ctx.stroke();
2755
+ const avatarImage = await loadImageSafe(url);
2756
+ ctx.save();
2757
+ ctx.beginPath();
2758
+ ctx.arc(cx, cy, AVATAR_SIZE2 / 2, 0, Math.PI * 2);
2759
+ ctx.clip();
2760
+ if (avatarImage) {
2761
+ ctx.drawImage(
2762
+ avatarImage,
2763
+ cx - AVATAR_SIZE2 / 2,
2764
+ cy - AVATAR_SIZE2 / 2,
2765
+ AVATAR_SIZE2,
2766
+ AVATAR_SIZE2
2767
+ );
2768
+ } else {
2769
+ ctx.fillStyle = "#2b2d33";
2770
+ ctx.fillRect(cx - AVATAR_SIZE2 / 2, cy - AVATAR_SIZE2 / 2, AVATAR_SIZE2, AVATAR_SIZE2);
2771
+ ctx.font = withFallback(`${Math.round(AVATAR_SIZE2 * 0.4)}px Helvetica Bold`);
2772
+ ctx.textAlign = "center";
2773
+ ctx.textBaseline = "middle";
2774
+ ctx.fillStyle = "rgba(255, 255, 255, 0.7)";
2775
+ ctx.fillText("?", cx, cy);
2776
+ }
2777
+ ctx.restore();
2778
+ }
2779
+
2780
+ // Src/Utils/ShipCard/render.ts
2781
+ async function drawShipCard(ctx, width, height, layout) {
2782
+ const accentColor = layout.accentColor ? parseHex(layout.accentColor) : DEFAULT_ACCENT4;
2783
+ const heartColor = layout.heartColor ? parseHex(layout.heartColor) : accentColor;
2784
+ ctx.save();
2785
+ ctx.beginPath();
2786
+ ctx.roundRect(0, 0, width, height, [CORNER_RADIUS5]);
2787
+ ctx.clip();
2788
+ ctx.fillStyle = "#0c0d11";
2789
+ ctx.fillRect(0, 0, width, height);
2790
+ const backgroundImage = await loadImageSafe(layout.customBackground);
2791
+ if (backgroundImage) {
2792
+ drawCoverImage(ctx, backgroundImage, 0, 0, width, height);
2793
+ ctx.fillStyle = "rgba(8, 9, 13, 0.5)";
2794
+ ctx.fillRect(0, 0, width, height);
2795
+ const duotone = ctx.createLinearGradient(0, 0, width, height);
2796
+ duotone.addColorStop(0, hexToRgba(accentColor, 0.25));
2797
+ duotone.addColorStop(1, "rgba(8, 9, 13, 0.35)");
2798
+ ctx.fillStyle = duotone;
2799
+ ctx.fillRect(0, 0, width, height);
2800
+ } else {
2801
+ const grd = ctx.createLinearGradient(0, 0, width, height);
2802
+ grd.addColorStop(0, hexToRgba(accentColor, 0.32));
2803
+ grd.addColorStop(0.55, "rgba(20, 16, 26, 1)");
2804
+ grd.addColorStop(1, "rgba(10, 11, 15, 1)");
2805
+ ctx.fillStyle = grd;
2806
+ ctx.fillRect(0, 0, width, height);
2807
+ const glow = ctx.createRadialGradient(
2808
+ width / 2,
2809
+ height * 0.62,
2810
+ 10,
2811
+ width / 2,
2812
+ height * 0.62,
2813
+ width * 0.4
2814
+ );
2815
+ glow.addColorStop(0, hexToRgba(accentColor, 0.35));
2816
+ glow.addColorStop(1, "rgba(0, 0, 0, 0)");
2817
+ ctx.fillStyle = glow;
2818
+ ctx.fillRect(0, 0, width, height);
2819
+ }
2820
+ drawVignette(ctx, width, height);
2821
+ drawSparkles(ctx, width, height, heartColor);
2822
+ const showText = layout.showText ?? true;
2823
+ if (showText) {
2824
+ const headerPanel = ctx.createLinearGradient(0, 0, 0, 96);
2825
+ headerPanel.addColorStop(0, "rgba(5, 5, 8, 0.55)");
2826
+ headerPanel.addColorStop(1, "rgba(5, 5, 8, 0)");
2827
+ ctx.fillStyle = headerPanel;
2828
+ ctx.fillRect(0, 0, width, 96);
2829
+ drawHeartIcon(ctx, 34, 32, 18, heartColor);
2830
+ ctx.textAlign = "left";
2831
+ ctx.font = withFallback("13px Helvetica");
2832
+ ctx.fillStyle = "rgba(255, 255, 255, 0.65)";
2833
+ ctx.fillText(`LA COMPATIBILIDAD ES ${layout.percentage}%`.toUpperCase(), 54, 37);
2834
+ ctx.font = withFallback("24px Helvetica Bold");
2835
+ ctx.shadowColor = "rgba(0, 0, 0, 0.6)";
2836
+ ctx.shadowBlur = 12;
2837
+ ctx.shadowOffsetY = 2;
2838
+ ctx.fillStyle = "#FFFFFF";
2839
+ ctx.fillText(truncateText(layout.message, 42), 32, 76);
2840
+ ctx.shadowColor = "transparent";
2841
+ ctx.shadowBlur = 0;
2842
+ ctx.shadowOffsetY = 0;
2843
+ if (layout.shipName) {
2844
+ drawShipNamePill(ctx, width, layout.shipName, accentColor);
2845
+ }
2846
+ }
2847
+ const bandY = showText ? 110 : 20;
2848
+ const bandHeight = height - bandY - (showText ? 26 : 20);
2849
+ const bandCy = bandY + bandHeight / 2;
2850
+ const leftCx = 24 + AVATAR_SIZE2 / 2 + 24;
2851
+ const rightCx = width - 24 - AVATAR_SIZE2 / 2 - 24;
2852
+ const heartWidth = Math.min(
2853
+ HEART_WIDTH,
2854
+ rightCx - AVATAR_SIZE2 / 2 - (leftCx + AVATAR_SIZE2 / 2) - 20
2855
+ );
2856
+ const heartHeight = Math.min(HEART_HEIGHT, bandHeight - 4);
2857
+ drawConnector(
2858
+ ctx,
2859
+ leftCx + AVATAR_SIZE2 / 2 + 10,
2860
+ width / 2 - heartWidth / 2 - 10,
2861
+ bandCy,
2862
+ heartColor
2863
+ );
2864
+ drawConnector(
2865
+ ctx,
2866
+ width / 2 + heartWidth / 2 + 10,
2867
+ rightCx - AVATAR_SIZE2 / 2 - 10,
2868
+ bandCy,
2869
+ heartColor
2870
+ );
2871
+ await drawAvatarCircle(ctx, leftCx, bandCy, layout.leftAvatarUrl, accentColor);
2872
+ await drawAvatarCircle(ctx, rightCx, bandCy, layout.rightAvatarUrl, accentColor);
2873
+ drawHeartMeter(ctx, width / 2, bandCy, heartWidth, heartHeight, layout.percentage, heartColor);
2874
+ ctx.restore();
2875
+ const borderColors = layout.noBorder ? [] : layout.borderColors?.length ? layout.borderColors : [accentColor];
2876
+ if (borderColors.length > 0) {
2877
+ drawGradientBorder(ctx, width, height, borderColors, CORNER_RADIUS5 - 2, 4);
2878
+ }
2879
+ }
2880
+
2881
+ // Src/Utils/shipCard.output.utils.ts
2882
+ async function genShipPng(layout, output) {
2883
+ const { width, height } = getCardDimensions4();
2884
+ const { canvas, ctx } = createShipCanvas(width, height);
2885
+ await drawShipCard(ctx, width, height, layout);
2886
+ return encodeCanvas(canvas, output);
2887
+ }
2888
+
2889
+ // Src/Cards/shipImage.ts
2890
+ async function resolveAvatarUrl(userId, override, guildId, bypassCache) {
2891
+ if (override) return parseImg(override);
2892
+ const data = await fetchUserData(userId, guildId, bypassCache);
2893
+ return data.assets.avatarURL ?? data.assets.defaultAvatarURL;
2894
+ }
2895
+ async function shipImage(leftUserId, rightUserId, options = {}) {
2896
+ if (!leftUserId || typeof leftUserId !== "string") {
2897
+ throw new KiraError("A valid leftUserId is required", "VALIDATION" /* Validation */);
2898
+ }
2899
+ if (!rightUserId || typeof rightUserId !== "string") {
2900
+ throw new KiraError("A valid rightUserId is required", "VALIDATION" /* Validation */);
2901
+ }
2902
+ const bypassCache = options.bypassCache ?? false;
2903
+ const [leftAvatarUrl, rightAvatarUrl] = await Promise.all([
2904
+ resolveAvatarUrl(leftUserId, options.leftImage, options.guildId, bypassCache),
2905
+ resolveAvatarUrl(rightUserId, options.rightImage, options.guildId, bypassCache)
2906
+ ]);
2907
+ const percentage = typeof options.percentage === "number" ? Math.min(100, Math.max(0, Math.round(options.percentage))) : computeCompatibility(leftUserId, rightUserId);
2908
+ const message = options.message ?? pickShipMessage(leftUserId, rightUserId, percentage);
2909
+ const palette = getThemePalette(options.theme);
2910
+ const accentColor = options.accentColor ?? palette?.accentColor;
2911
+ const needsDecoration = Boolean(options.useNitroTheme || options.useRoleColor);
2912
+ const leftData = needsDecoration ? await fetchUserData(leftUserId, options.guildId, bypassCache) : void 0;
2913
+ const borderColors = resolveCardColors({
2914
+ custom: options.borderColor,
2915
+ removeBorder: options.removeBorder,
2916
+ useNitroTheme: options.useNitroTheme,
2917
+ nitroColors: leftData?.decoration.profileColors,
2918
+ useRoleColor: options.useRoleColor,
2919
+ roleColor: leftData?.decoration.roleColor,
2920
+ fallback: palette?.borderColor ?? (accentColor ? [accentColor] : void 0)
2921
+ });
2922
+ const layout = {
2923
+ leftAvatarUrl,
2924
+ rightAvatarUrl,
2925
+ percentage,
2926
+ message,
2927
+ accentColor,
2928
+ borderColors: borderColors.length ? borderColors : void 0,
2929
+ noBorder: options.removeBorder,
2930
+ customBackground: options.customBackground,
2931
+ heartColor: options.heartColor ?? palette?.heartColor,
2932
+ shipName: options.shipName,
2933
+ showText: options.showText
2934
+ };
2935
+ try {
2936
+ return await genShipPng(layout, options.output);
2937
+ } catch (error) {
2938
+ if (error instanceof KiraError) throw error;
2939
+ const message2 = error instanceof Error ? error.message : String(error);
2940
+ throw new KiraError(message2, "RENDER" /* Render */);
2941
+ }
2942
+ }
2943
+
2944
+ // Src/Utils/attachment.utils.ts
2945
+ import { AttachmentBuilder } from "discord.js";
2946
+ function toAttachment(buffer, name = "card", format) {
2947
+ const fileName = name.includes(".") ? name : `${name}.${extensionForFormat(format)}`;
2948
+ return new AttachmentBuilder(buffer, { name: fileName });
2949
+ }
2950
+ export {
2951
+ KiraError,
2952
+ KiraErrorCode,
2953
+ THEMES,
2954
+ achievementImage,
2955
+ clearCache,
2956
+ computeCompatibility,
2957
+ decimalToHex,
2958
+ drawCoverImage,
2959
+ drawGradientBorder,
2960
+ encodeCanvas,
2961
+ extensionForFormat,
2962
+ getCacheSize,
2963
+ getThemePalette,
2964
+ hexToRgb,
2965
+ hexToRgba,
2966
+ isNumber,
2967
+ isString,
2968
+ leaderboardImage,
2969
+ leaveImage,
2970
+ levelUpImage,
2971
+ loadImageSafe,
2972
+ parseHex,
2973
+ parseImg,
2974
+ parsePng,
2975
+ pickShipMessage,
2976
+ profileImage,
2977
+ setCacheOptions,
2978
+ setClient,
2979
+ shipImage,
2980
+ toAttachment,
2981
+ welcomeImage
2982
+ };
2983
+ //# sourceMappingURL=index.js.map