@skyhelperbot/utils 2.1.0 → 2.2.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 +3 -4
- package/dist/{classes → types/classes}/SkytimesUtils.d.ts +4 -5
- package/dist/types/classes/index.d.ts +3 -0
- package/dist/types/classes/shardsUtil.d.ts +77 -0
- package/dist/{classes → types/classes}/utils.d.ts +3 -0
- package/dist/types/constants/eventDatas.d.ts +31 -0
- package/dist/{constants → types/constants}/index.d.ts +1 -0
- package/dist/{constants → types/constants}/shardsInfo.d.ts +12 -16
- package/dist/{constants → types/constants}/shardsTimeline.d.ts +3 -2
- package/dist/{index.d.ts → types/index.d.ts} +1 -0
- package/dist/types/typings.d.ts +29 -0
- package/dist/types/utils/PermissionUtils.d.ts +84 -0
- package/dist/types/utils/getNextTs.d.ts +10 -0
- package/dist/types/utils/index.d.ts +16 -0
- package/dist/{utils → types/utils}/parseDateFormat.d.ts +1 -0
- package/dist/{utils → types/utils}/parsePerms.d.ts +2 -0
- package/dist/{utils → types/utils}/postToBin.d.ts +1 -0
- package/dist/{utils → types/utils}/recursiveReadDir.d.ts +1 -0
- package/dist/{utils → types/utils}/resolveColors.d.ts +1 -0
- package/dist/types/utils/v2-builders.d.ts +14 -0
- package/package.json +17 -11
- package/dist/classes/LeaderBoardCard.d.ts +0 -59
- package/dist/classes/LeaderBoardCard.js +0 -249
- package/dist/classes/SkytimesUtils.js +0 -116
- package/dist/classes/WinnerCard.d.ts +0 -13
- package/dist/classes/WinnerCard.js +0 -117
- package/dist/classes/index.d.ts +0 -5
- package/dist/classes/index.js +0 -5
- package/dist/classes/shardsUtil.d.ts +0 -40
- package/dist/classes/shardsUtil.js +0 -122
- package/dist/classes/utils.js +0 -28
- package/dist/constants/eventDatas.d.ts +0 -31
- package/dist/constants/eventDatas.js +0 -105
- package/dist/constants/index.js +0 -5
- package/dist/constants/shardsInfo.js +0 -424
- package/dist/constants/shardsTimeline.js +0 -46
- package/dist/typings.d.ts +0 -58
- package/dist/typings.js +0 -1
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.js +0 -5
- package/dist/utils/parseDateFormat.js +0 -25
- package/dist/utils/parsePerms.js +0 -54
- package/dist/utils/postToBin.js +0 -15
- package/dist/utils/recursiveReadDir.js +0 -37
- package/dist/utils/resolveColors.js +0 -63
- package/shared/assets/Point.png +0 -0
- package/shared/assets/Win.png +0 -0
- package/shared/assets/medal_champion_award_winner_olympic_icon_207790.png +0 -0
- package/shared/assets/server.svg +0 -41
- package/shared/fonts/circularstd-black.otf +0 -0
- package/shared/fonts/notoemoji-bold.ttf +0 -0
- package/shared/fonts/notosans-black.ttf +0 -0
- package/shared/fonts/notosans-jp-black.ttf +0 -0
- package/shared/fonts/notosans-kr-black.ttf +0 -0
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { userData, colorsType, LeaderboardOptions } from "../typings.js";
|
|
2
|
-
/**
|
|
3
|
-
* Represents a Quiz Leaderboard Card. A class for generating a leaderboard card for a quiz or game.
|
|
4
|
-
* @returns - The generated image buffer.
|
|
5
|
-
*/
|
|
6
|
-
export declare class LeaderboardCard {
|
|
7
|
-
private usersData;
|
|
8
|
-
private background;
|
|
9
|
-
private abbreviateNumber;
|
|
10
|
-
private opacity;
|
|
11
|
-
private scoreMessage;
|
|
12
|
-
private colors;
|
|
13
|
-
constructor(options: LeaderboardOptions);
|
|
14
|
-
/**
|
|
15
|
-
* .setUsersData
|
|
16
|
-
* @param usersData ```js
|
|
17
|
-
* [{ top: int, avatar: "string", tag: "string", score: int}, games: int]
|
|
18
|
-
* ```
|
|
19
|
-
* @example setUsersData([{top:1,avatar:"https://someone-image.png",tag:"fivesobes",score:5, games:8}])
|
|
20
|
-
*/
|
|
21
|
-
setUsersData(usersData: userData[]): this;
|
|
22
|
-
/**
|
|
23
|
-
* .setScoreMessage
|
|
24
|
-
* @param message Set Custom Score Message
|
|
25
|
-
* @example setScoreMessage("Message")
|
|
26
|
-
*/
|
|
27
|
-
setScoreMessage(message: string): this;
|
|
28
|
-
/**
|
|
29
|
-
* .setColors
|
|
30
|
-
* @param colors ```json
|
|
31
|
-
* {box: "hexcolor", username: "hexcolor", score: "hexcolor", firstRank: "hexcolor", secondRank: "hexcolor", thirdRank: "hexcolor"}
|
|
32
|
-
* ```
|
|
33
|
-
* @example
|
|
34
|
-
* setColors({ box: '#212121', username: '#ffffff', score: '#ffffff', firstRank: '#f7c716', secondRank: '#9e9e9e', thirdRank: '#94610f' })
|
|
35
|
-
*/
|
|
36
|
-
setColors(colors: colorsType): this;
|
|
37
|
-
/**
|
|
38
|
-
* .setabbreviateNumber
|
|
39
|
-
* @param bool must be "true" or "false"
|
|
40
|
-
* @example setabbreviateNumber(true)
|
|
41
|
-
*/
|
|
42
|
-
setabbreviateNumber(bool: boolean): this;
|
|
43
|
-
/**
|
|
44
|
-
* .setOpacity
|
|
45
|
-
* @param opacity must be between 0 and 1
|
|
46
|
-
* @example setOpacity(0.6)
|
|
47
|
-
*/
|
|
48
|
-
setOpacity(opacity?: number): this;
|
|
49
|
-
/**
|
|
50
|
-
* .setBackground
|
|
51
|
-
* @param type "image" or "color"
|
|
52
|
-
* @param value "url" or "hexcolor"
|
|
53
|
-
* @example setBackground("image","https://someone-image.png")
|
|
54
|
-
* @example setBackground("color","#000")
|
|
55
|
-
*/
|
|
56
|
-
setBackground(type: string, value: string): this;
|
|
57
|
-
build(): Promise<Buffer>;
|
|
58
|
-
private fillRoundRect;
|
|
59
|
-
}
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import { createCanvas, loadImage, GlobalFonts } from "@napi-rs/canvas";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
/**
|
|
4
|
-
* Represents a Quiz Leaderboard Card. A class for generating a leaderboard card for a quiz or game.
|
|
5
|
-
* @returns - The generated image buffer.
|
|
6
|
-
*/
|
|
7
|
-
export class LeaderboardCard {
|
|
8
|
-
usersData;
|
|
9
|
-
background;
|
|
10
|
-
abbreviateNumber;
|
|
11
|
-
opacity;
|
|
12
|
-
scoreMessage;
|
|
13
|
-
colors;
|
|
14
|
-
constructor(options) {
|
|
15
|
-
this.usersData = options?.usersData || [];
|
|
16
|
-
this.background = {
|
|
17
|
-
type: "none",
|
|
18
|
-
background: "none",
|
|
19
|
-
};
|
|
20
|
-
this.abbreviateNumber = false;
|
|
21
|
-
this.opacity = 0;
|
|
22
|
-
this.scoreMessage = "";
|
|
23
|
-
this.colors = options?.colors || {
|
|
24
|
-
box: "#212121",
|
|
25
|
-
username: "#ffffff",
|
|
26
|
-
score: "#ffffff",
|
|
27
|
-
firstRank: "#f7c716",
|
|
28
|
-
secondRank: "#9e9e9e",
|
|
29
|
-
thirdRank: "#94610f",
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* .setUsersData
|
|
34
|
-
* @param usersData ```js
|
|
35
|
-
* [{ top: int, avatar: "string", tag: "string", score: int}, games: int]
|
|
36
|
-
* ```
|
|
37
|
-
* @example setUsersData([{top:1,avatar:"https://someone-image.png",tag:"fivesobes",score:5, games:8}])
|
|
38
|
-
*/
|
|
39
|
-
setUsersData(usersData) {
|
|
40
|
-
if (usersData.length > 10) {
|
|
41
|
-
throw new Error("setUsersData values cannot be greater than 10.");
|
|
42
|
-
}
|
|
43
|
-
this.usersData = usersData;
|
|
44
|
-
return this;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* .setScoreMessage
|
|
48
|
-
* @param message Set Custom Score Message
|
|
49
|
-
* @example setScoreMessage("Message")
|
|
50
|
-
*/
|
|
51
|
-
setScoreMessage(message) {
|
|
52
|
-
this.scoreMessage = message;
|
|
53
|
-
return this;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* .setColors
|
|
57
|
-
* @param colors ```json
|
|
58
|
-
* {box: "hexcolor", username: "hexcolor", score: "hexcolor", firstRank: "hexcolor", secondRank: "hexcolor", thirdRank: "hexcolor"}
|
|
59
|
-
* ```
|
|
60
|
-
* @example
|
|
61
|
-
* setColors({ box: '#212121', username: '#ffffff', score: '#ffffff', firstRank: '#f7c716', secondRank: '#9e9e9e', thirdRank: '#94610f' })
|
|
62
|
-
*/
|
|
63
|
-
setColors(colors) {
|
|
64
|
-
this.colors = colors;
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* .setabbreviateNumber
|
|
69
|
-
* @param bool must be "true" or "false"
|
|
70
|
-
* @example setabbreviateNumber(true)
|
|
71
|
-
*/
|
|
72
|
-
setabbreviateNumber(bool) {
|
|
73
|
-
if (typeof bool !== "boolean") {
|
|
74
|
-
throw new Error("You must give a abbreviate number true or false argument.");
|
|
75
|
-
}
|
|
76
|
-
this.abbreviateNumber = bool;
|
|
77
|
-
return this;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* .setOpacity
|
|
81
|
-
* @param opacity must be between 0 and 1
|
|
82
|
-
* @example setOpacity(0.6)
|
|
83
|
-
*/
|
|
84
|
-
setOpacity(opacity = 0) {
|
|
85
|
-
if (opacity >= 0 && opacity <= 1) {
|
|
86
|
-
this.opacity = opacity;
|
|
87
|
-
return this;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
throw new Error("The value of the opacity of setOpacity method must be between 0 and 1 (0 and 1 included).");
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* .setBackground
|
|
95
|
-
* @param type "image" or "color"
|
|
96
|
-
* @param value "url" or "hexcolor"
|
|
97
|
-
* @example setBackground("image","https://someone-image.png")
|
|
98
|
-
* @example setBackground("color","#000")
|
|
99
|
-
*/
|
|
100
|
-
setBackground(type, value) {
|
|
101
|
-
if (type === "color") {
|
|
102
|
-
if (/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(value)) {
|
|
103
|
-
this.background.type = "color";
|
|
104
|
-
this.background.background = value;
|
|
105
|
-
return this;
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
throw new Error("Invalid color for the second argument in setForeground method. You must give a hexadecimal color.");
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
else if (type === "image") {
|
|
112
|
-
this.background.type = "image";
|
|
113
|
-
this.background.background = value;
|
|
114
|
-
return this;
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
throw new Error("The first argument of setBackground must be 'color' or 'image'.");
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
async build() {
|
|
121
|
-
GlobalFonts.registerFromPath(path.join(import.meta.dirname, `../../shared/fonts/circularstd-black.otf`), "circular-std");
|
|
122
|
-
GlobalFonts.registerFromPath(path.join(import.meta.dirname, `../../shared/fonts/notosans-jp-black.ttf`), "noto-sans-jp");
|
|
123
|
-
GlobalFonts.registerFromPath(path.join(import.meta.dirname, `../../shared/fonts/notosans-black.ttf`), "noto-sans");
|
|
124
|
-
GlobalFonts.registerFromPath(path.join(import.meta.dirname, `../../shared/fonts/notoemoji-bold.ttf`), "noto-emoji");
|
|
125
|
-
GlobalFonts.registerFromPath(path.join(import.meta.dirname, `../../shared/fonts/notosans-kr-black.ttf`), "noto-sans-kr");
|
|
126
|
-
const abbreviateNumber = (value) => {
|
|
127
|
-
let newValue = value;
|
|
128
|
-
if (value >= 1000) {
|
|
129
|
-
const suffixes = ["", "K", "M", "B", "T"];
|
|
130
|
-
const suffixNum = Math.floor(("" + value).length / 3);
|
|
131
|
-
let shortValue = "";
|
|
132
|
-
for (let precision = 2; precision >= 1; precision--) {
|
|
133
|
-
shortValue = parseFloat((suffixNum != 0 ? value / Math.pow(1000, suffixNum) : value).toPrecision(precision));
|
|
134
|
-
const dotLessShortValue = (shortValue + "").replace(/[^a-zA-Z 0-9]+/g, "");
|
|
135
|
-
if (dotLessShortValue.length <= 2) {
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (typeof shortValue === "number" && shortValue % 1 != 0)
|
|
140
|
-
shortValue = shortValue.toFixed(1);
|
|
141
|
-
newValue = shortValue + suffixes[suffixNum];
|
|
142
|
-
}
|
|
143
|
-
return newValue.toString();
|
|
144
|
-
};
|
|
145
|
-
const yuksek = this.usersData.length * 74.5;
|
|
146
|
-
const canvas = createCanvas(680, yuksek);
|
|
147
|
-
const ctx = canvas.getContext("2d");
|
|
148
|
-
ctx.globalAlpha = 1;
|
|
149
|
-
if (this.background.type === "color") {
|
|
150
|
-
ctx.fillStyle = this.background.background;
|
|
151
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
152
|
-
}
|
|
153
|
-
else if (this.background.type === "image") {
|
|
154
|
-
try {
|
|
155
|
-
ctx.drawImage(await loadImage(this.background.background), 0, 0, canvas.width, canvas.height);
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
throw new Error("The image given in the second parameter of the setBackground method is not valid or you are not connected to the internet.");
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
if (this.usersData) {
|
|
162
|
-
let Box_Y = 0, Avatar_Y = 0, Tag_Y = 45, XP_Y = 45, Level_Y = 30, Rank_Y = 45;
|
|
163
|
-
for (let i = 0; i < this.usersData.length; i++) {
|
|
164
|
-
ctx.save();
|
|
165
|
-
ctx.fillStyle = this.colors.box;
|
|
166
|
-
ctx.globalAlpha = this.opacity;
|
|
167
|
-
this.fillRoundRect(ctx, 0, Box_Y, canvas.width, 70, 15, true, false);
|
|
168
|
-
ctx.globalAlpha = 1;
|
|
169
|
-
try {
|
|
170
|
-
const avatar = await loadImage(this.usersData[i].avatar);
|
|
171
|
-
ctx.clip();
|
|
172
|
-
ctx.drawImage(avatar, 0, Avatar_Y, 70, 70);
|
|
173
|
-
}
|
|
174
|
-
catch (err) {
|
|
175
|
-
console.error("Failed to load avatar", err);
|
|
176
|
-
}
|
|
177
|
-
ctx.shadowBlur = 10;
|
|
178
|
-
ctx.shadowOffsetX = 8;
|
|
179
|
-
ctx.shadowOffsetY = 6;
|
|
180
|
-
ctx.shadowColor = "#0a0a0a";
|
|
181
|
-
ctx.fillStyle = this.colors.username;
|
|
182
|
-
ctx.font = `bold 25px circular-std, noto-emoji, noto-sans-jp, noto-sans, noto-sans-kr`;
|
|
183
|
-
ctx.textAlign = "left";
|
|
184
|
-
ctx.fillText(this.usersData[i].tag, 80, Tag_Y, 260);
|
|
185
|
-
ctx.fillStyle = this.colors.score;
|
|
186
|
-
ctx.font = `bold 20px circular-std, noto-emoji, noto-sans-jp, noto-sans, noto-sans-kr`;
|
|
187
|
-
ctx.textAlign = "right";
|
|
188
|
-
ctx.fillText(`${this.scoreMessage} ${this.abbreviateNumber == true ? `${abbreviateNumber(this.usersData[i].score)}` : `${this.usersData[i].score}`}/${this.abbreviateNumber == true ? `${abbreviateNumber(this.usersData[i].games)}` : `${this.usersData[i].games}`}`, 560, XP_Y, 200);
|
|
189
|
-
if (this.usersData[i].top === 1) {
|
|
190
|
-
ctx.fillStyle = this.colors.firstRank;
|
|
191
|
-
}
|
|
192
|
-
else if (this.usersData[i].top === 2) {
|
|
193
|
-
ctx.fillStyle = this.colors.secondRank;
|
|
194
|
-
}
|
|
195
|
-
else if (this.usersData[i].top === 3) {
|
|
196
|
-
ctx.fillStyle = this.colors.thirdRank;
|
|
197
|
-
}
|
|
198
|
-
ctx.font = `bold 30px circular-std, noto-emoji, noto-sans-jp, noto-sans, noto-sans-kr`;
|
|
199
|
-
ctx.textAlign = "right";
|
|
200
|
-
ctx.fillText("#" + this.usersData[i].top, 660, Rank_Y, 75);
|
|
201
|
-
Box_Y = Box_Y + 75;
|
|
202
|
-
Avatar_Y = Avatar_Y + 75;
|
|
203
|
-
Tag_Y = Tag_Y + 75;
|
|
204
|
-
XP_Y = XP_Y + 75;
|
|
205
|
-
Level_Y = Level_Y + 75;
|
|
206
|
-
Rank_Y = Rank_Y + 75;
|
|
207
|
-
ctx.restore();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
ctx.font = `bold 40px circular-std, noto-emoji, noto-sans-jp, noto-sans, noto-sans-kr`;
|
|
212
|
-
ctx.fillStyle = "#ffffff";
|
|
213
|
-
ctx.textAlign = "center";
|
|
214
|
-
ctx.shadowBlur = 10;
|
|
215
|
-
ctx.shadowOffsetX = 8;
|
|
216
|
-
ctx.shadowOffsetY = 6;
|
|
217
|
-
ctx.shadowColor = "#0a0a0a";
|
|
218
|
-
ctx.fillText("Not found!", 340, 370, 500);
|
|
219
|
-
}
|
|
220
|
-
return canvas.toBuffer("image/png");
|
|
221
|
-
}
|
|
222
|
-
fillRoundRect(ctx, x, y, w, h, r, f, s) {
|
|
223
|
-
if (typeof r === "number") {
|
|
224
|
-
r = { tl: r, tr: r, br: r, bl: r };
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
const defaultRadius = { tl: 0, tr: 0, br: 0, bl: 0 };
|
|
228
|
-
for (const side in defaultRadius) {
|
|
229
|
-
r[side] =
|
|
230
|
-
r[side] || defaultRadius[side];
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
ctx.beginPath();
|
|
234
|
-
ctx.moveTo(x + r.tl, y);
|
|
235
|
-
ctx.lineTo(x + w - r.tr, y);
|
|
236
|
-
ctx.quadraticCurveTo(x + w, y, x + w, y + r.tr);
|
|
237
|
-
ctx.lineTo(x + w, y + h - r.br);
|
|
238
|
-
ctx.quadraticCurveTo(x + w, y + h, x + w - r.br, y + h);
|
|
239
|
-
ctx.lineTo(x + r.bl, y + h);
|
|
240
|
-
ctx.quadraticCurveTo(x, y + h, x, y + h - r.bl);
|
|
241
|
-
ctx.lineTo(x, y + r.tl);
|
|
242
|
-
ctx.quadraticCurveTo(x, y, x + r.tl, y);
|
|
243
|
-
ctx.closePath();
|
|
244
|
-
if (f)
|
|
245
|
-
ctx.fill();
|
|
246
|
-
if (s)
|
|
247
|
-
ctx.stroke();
|
|
248
|
-
}
|
|
249
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { eventData } from "../constants/eventDatas.js";
|
|
2
|
-
import { DateTime } from "luxon";
|
|
3
|
-
/**
|
|
4
|
-
* Utilities for skytimes
|
|
5
|
-
*/
|
|
6
|
-
export class SkytimesUtils {
|
|
7
|
-
/** Get all occurrences of an event for the given date
|
|
8
|
-
* @param eventTime The DateTime for which to get all occurrences
|
|
9
|
-
* @param interval The interval between the event occurrences
|
|
10
|
-
*/
|
|
11
|
-
static getAllTimes(eventTime, interval) {
|
|
12
|
-
let clonedTime = eventTime;
|
|
13
|
-
const timeBuilt = [];
|
|
14
|
-
while (eventTime.hasSame(clonedTime, "day")) {
|
|
15
|
-
timeBuilt.push(`<t:${clonedTime.toUnixInteger()}:t>`);
|
|
16
|
-
clonedTime = clonedTime.plus({ minutes: interval || 0 });
|
|
17
|
-
}
|
|
18
|
-
return timeBuilt.join(" • ");
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Get the date in DateTime on which the event will occur (if it's not a daily event)
|
|
22
|
-
* @param event The event for which to get the date-time
|
|
23
|
-
*/
|
|
24
|
-
static getOccurrenceDay(event) {
|
|
25
|
-
let nextOccurrence = DateTime.now().setZone("America/Los_Angeles").startOf("day").plus({ minutes: event.offset }); // Start with the offset from the beginning of the day
|
|
26
|
-
if (event.occursOn) {
|
|
27
|
-
// If the event occurs on specific weekdays
|
|
28
|
-
if (event.occursOn.weekDays) {
|
|
29
|
-
while (!event.occursOn.weekDays.includes(nextOccurrence.weekday)) {
|
|
30
|
-
nextOccurrence = nextOccurrence.plus({ days: 1 });
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
// If the event occurs on a specific day of the month
|
|
34
|
-
if (event.occursOn.dayOfTheMonth) {
|
|
35
|
-
while (nextOccurrence.day !== event.occursOn.dayOfTheMonth) {
|
|
36
|
-
nextOccurrence = nextOccurrence.plus({ days: 1 });
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return nextOccurrence;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Get the next occurrence of the event relative to now
|
|
44
|
-
* @param eventName The key of the event
|
|
45
|
-
*/
|
|
46
|
-
static getNextEventOccurrence(eventName) {
|
|
47
|
-
const event = eventData[eventName];
|
|
48
|
-
if (!event)
|
|
49
|
-
throw new Error("Unknown Event");
|
|
50
|
-
const now = DateTime.now().setZone("America/Los_Angeles"); // Current time
|
|
51
|
-
let nextOccurrence = this.getOccurrenceDay(event);
|
|
52
|
-
// Loop to calculate the next occurrence based on the interval
|
|
53
|
-
while (nextOccurrence < now) {
|
|
54
|
-
nextOccurrence = nextOccurrence.plus({ minutes: event.interval || 0 });
|
|
55
|
-
}
|
|
56
|
-
return nextOccurrence;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Get the details about an event, their status, next occurrence, all occurrences for the day
|
|
60
|
-
* @param key The event key
|
|
61
|
-
*/
|
|
62
|
-
static getEventDetails(key) {
|
|
63
|
-
const nextOccurence = this.getNextEventOccurrence(key);
|
|
64
|
-
return {
|
|
65
|
-
event: eventData[key],
|
|
66
|
-
nextOccurence,
|
|
67
|
-
allOccurences: this.getAllTimes(this.getOccurrenceDay(eventData[key]), eventData[key].interval),
|
|
68
|
-
status: this.getEventStatus(eventData[key], nextOccurence),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Same as {@apilink SkytimesUtils.getEventDetails | getEventDetails} but for all of the events
|
|
73
|
-
* @returns An array of event details
|
|
74
|
-
*/
|
|
75
|
-
static allEventDetails() {
|
|
76
|
-
const keys = Object.keys(eventData).sort((a, b) => eventData[a].index - eventData[b].index);
|
|
77
|
-
const occurrences = [];
|
|
78
|
-
for (const key of keys) {
|
|
79
|
-
occurrences.push([key, this.getEventDetails(key)]);
|
|
80
|
-
}
|
|
81
|
-
return occurrences;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Returns the event status of a given event
|
|
85
|
-
* @param event The event to get the status for
|
|
86
|
-
* @param nextOccurrence The next occurrence of the event relative to "now"
|
|
87
|
-
* @returns The event status (or null if there is no active duration)
|
|
88
|
-
*/
|
|
89
|
-
static getEventStatus(event, nextOccurrence) {
|
|
90
|
-
const now = DateTime.now().setZone("America/Los_Angeles");
|
|
91
|
-
const BASE = {
|
|
92
|
-
active: false,
|
|
93
|
-
nextTime: nextOccurrence,
|
|
94
|
-
duration: nextOccurrence.diff(now).toFormat("d'd' h'h' m'm' s's'"),
|
|
95
|
-
};
|
|
96
|
-
if (!event.duration)
|
|
97
|
-
return BASE;
|
|
98
|
-
// Subtract the interval because nextOccurrence always calculates the next upcoming event
|
|
99
|
-
// So we subtract the interval to get the last occurrence, and add the active duration to it, and check if now is between those
|
|
100
|
-
const start = nextOccurrence.minus({ minutes: event.interval || 0 });
|
|
101
|
-
const end = start.plus({ minutes: event.duration });
|
|
102
|
-
// When active
|
|
103
|
-
if (now >= start && now <= end) {
|
|
104
|
-
return {
|
|
105
|
-
active: true,
|
|
106
|
-
startTime: start,
|
|
107
|
-
endTime: end,
|
|
108
|
-
nextTime: nextOccurrence,
|
|
109
|
-
duration: end.diff(now).toFormat("d'd' h'h' m'm' s's'"),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
return BASE;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { APIGuildMember, APIUser } from "discord-api-types/v10";
|
|
2
|
-
export declare class GameWinnerCard {
|
|
3
|
-
readonly clientAvatar: string;
|
|
4
|
-
private name;
|
|
5
|
-
private thumbnail;
|
|
6
|
-
private points;
|
|
7
|
-
private total;
|
|
8
|
-
constructor(winner: APIUser | APIGuildMember, wins: number, total: number, clientAvatar: string);
|
|
9
|
-
private path;
|
|
10
|
-
private roundRect;
|
|
11
|
-
private changeFontSize;
|
|
12
|
-
build(): Promise<Buffer>;
|
|
13
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { createCanvas, loadImage } from "@napi-rs/canvas";
|
|
2
|
-
import { colors, fancyCount, getUserAvatar } from "./utils.js";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
const size = 100;
|
|
5
|
-
// TODO: Integrate it with other game types
|
|
6
|
-
export class GameWinnerCard {
|
|
7
|
-
clientAvatar;
|
|
8
|
-
name;
|
|
9
|
-
thumbnail;
|
|
10
|
-
points;
|
|
11
|
-
total;
|
|
12
|
-
constructor(winner, wins, total, clientAvatar) {
|
|
13
|
-
this.clientAvatar = clientAvatar;
|
|
14
|
-
if ("user" in winner) {
|
|
15
|
-
this.name = winner.nick || winner.user?.global_name || winner?.user.username;
|
|
16
|
-
this.thumbnail = getUserAvatar(winner.user);
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
this.name = winner?.global_name || winner?.username;
|
|
20
|
-
this.thumbnail = getUserAvatar(winner);
|
|
21
|
-
}
|
|
22
|
-
this.points = wins;
|
|
23
|
-
this.total = total;
|
|
24
|
-
}
|
|
25
|
-
path(strs) {
|
|
26
|
-
return join(import.meta.dirname, strs);
|
|
27
|
-
}
|
|
28
|
-
roundRect(ctx, x, y, w, h, r) {
|
|
29
|
-
if (w < 2 * r)
|
|
30
|
-
r = w / 2;
|
|
31
|
-
if (h < 2 * r)
|
|
32
|
-
r = h / 2;
|
|
33
|
-
ctx.beginPath();
|
|
34
|
-
ctx.moveTo(x + r, y);
|
|
35
|
-
ctx.arcTo(x + w, y, x + w, y + h, r);
|
|
36
|
-
ctx.arcTo(x + w, y + h, x, y + h, r);
|
|
37
|
-
ctx.arcTo(x, y + h, x, y, r);
|
|
38
|
-
ctx.arcTo(x, y, x + w, y, r);
|
|
39
|
-
ctx.closePath();
|
|
40
|
-
return ctx;
|
|
41
|
-
}
|
|
42
|
-
changeFontSize(ctx, _size) {
|
|
43
|
-
const fontArgs = ctx.font.split(" ");
|
|
44
|
-
ctx.font = `${_size} ${fontArgs.slice(1).join(" ")}`; // / using the last part
|
|
45
|
-
return ctx;
|
|
46
|
-
}
|
|
47
|
-
async build() {
|
|
48
|
-
const canvas = createCanvas(16 * size, 5 * size);
|
|
49
|
-
const ctx = canvas.getContext("2d");
|
|
50
|
-
const { width: w, height: h } = canvas;
|
|
51
|
-
ctx.font = "85px SegoeUI, SegoeUIEmoji";
|
|
52
|
-
ctx.fillStyle = colors.darkgrey;
|
|
53
|
-
this.roundRect(ctx, 0, 0, w, h, size * 0.75).clip();
|
|
54
|
-
ctx.fill();
|
|
55
|
-
// box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
|
|
56
|
-
// ctx.shadowOffsetY = 40;
|
|
57
|
-
// ctx.shadowColor = 'rgba(0, 0, 0, 0.25)';
|
|
58
|
-
// ctx.shadowOffsetX = 40;
|
|
59
|
-
// Avatar
|
|
60
|
-
//
|
|
61
|
-
ctx.save();
|
|
62
|
-
ctx.lineWidth = 6;
|
|
63
|
-
ctx.beginPath();
|
|
64
|
-
ctx.arc(h / 2, h / 2, h * 0.3, 0, Math.PI * 2, true);
|
|
65
|
-
ctx.closePath();
|
|
66
|
-
ctx.clip();
|
|
67
|
-
ctx.fillStyle = colors.grey;
|
|
68
|
-
ctx.fillRect(h * 0.2, h * 0.2, h * 0.6, h * 0.6);
|
|
69
|
-
ctx.drawImage(await loadImage(this.thumbnail), h * 0.2, h * 0.2, h * 0.6, h * 0.6);
|
|
70
|
-
ctx.restore();
|
|
71
|
-
// Status
|
|
72
|
-
ctx.save();
|
|
73
|
-
ctx.translate(size * 3.55, size * 3.55);
|
|
74
|
-
ctx.fillStyle = colors.darkgrey;
|
|
75
|
-
ctx.beginPath();
|
|
76
|
-
ctx.arc(0, 0, size * 0.4, 0, Math.PI * 2, true);
|
|
77
|
-
ctx.closePath();
|
|
78
|
-
ctx.fill();
|
|
79
|
-
ctx.drawImage(await loadImage(this.path("../../shared/assets/Win.png")), -size * 0.3, -size * 0.3, size * 0.6, size * 0.6);
|
|
80
|
-
ctx.restore();
|
|
81
|
-
// User Text
|
|
82
|
-
//
|
|
83
|
-
ctx.fillStyle = colors.blue;
|
|
84
|
-
this.changeFontSize(ctx, h * 0.17 + "px");
|
|
85
|
-
ctx.fillText(this.name, w * 0.3, h * 0.45, w * 0.425);
|
|
86
|
-
ctx.fillStyle = colors.idle;
|
|
87
|
-
ctx.fillText("#", w * 0.3 + w * 0.4375, h * 0.45);
|
|
88
|
-
ctx.fillStyle = colors.online;
|
|
89
|
-
ctx.fillText("1", w * 0.3 + w * 0.425 + w * 0.05, h * 0.45, w * 0.15625);
|
|
90
|
-
// Botlist URL
|
|
91
|
-
//
|
|
92
|
-
// ctx.textBaseline = 'middle';
|
|
93
|
-
ctx.textAlign = "center";
|
|
94
|
-
ctx.fillStyle = colors.blue;
|
|
95
|
-
this.roundRect(ctx, w * 0.5, -(w * 0.0625), w * 0.5, h * 0.4, w * 0.0625);
|
|
96
|
-
ctx.fill();
|
|
97
|
-
ctx.fillRect(w * 0.625, 0, w * 0.5, w * 0.0625);
|
|
98
|
-
ctx.fillStyle = colors.darkgrey;
|
|
99
|
-
this.changeFontSize(ctx, h * 0.15 + "px");
|
|
100
|
-
ctx.fillText("Sky CoTL Quiz Game", w * 0.55 + h * 0.65, h * 0.15, w * 0.421875);
|
|
101
|
-
// Counters
|
|
102
|
-
//
|
|
103
|
-
ctx.textAlign = "left";
|
|
104
|
-
ctx.fillStyle = colors.lightgrey;
|
|
105
|
-
ctx.drawImage(await loadImage(this.path("../../shared/assets/Point.png")), w * 0.3, h * 0.65, w * 0.0625, w * 0.0625);
|
|
106
|
-
this.changeFontSize(ctx, h * 0.15 + "px");
|
|
107
|
-
ctx.fillText(`${fancyCount(this.points)}/${fancyCount(this.total)} points`, w * 0.25 + h * 0.45, h * 0.65 + h * 0.15, h * 10);
|
|
108
|
-
// ctx.drawImage(await loadImage(this.path('../../shared/assets/vote.svg')), w * 0.6, h * 0.65, w * 0.0625, w * 0.0625);
|
|
109
|
-
// ctx.fillText(fancyCount(this.total), w * 0.55 + h * 0.45, h * 0.65 + h * 0.15, h * 0.45);
|
|
110
|
-
// Library
|
|
111
|
-
ctx.fillStyle = colors.grey;
|
|
112
|
-
this.roundRect(ctx, w * 0.875, h * 0.6, h * 0.4, h * 0.4, h * 0.15).clip();
|
|
113
|
-
ctx.fill();
|
|
114
|
-
ctx.drawImage(await loadImage(this.clientAvatar), w * 0.875, h * 0.6, h * 0.4, h * 0.4);
|
|
115
|
-
return canvas.toBuffer("image/png");
|
|
116
|
-
}
|
|
117
|
-
}
|
package/dist/classes/index.d.ts
DELETED
package/dist/classes/index.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { DateTime } from "luxon";
|
|
2
|
-
import type { ShardsCountdown } from "../typings.js";
|
|
3
|
-
/**
|
|
4
|
-
* Sequence of Shards pattern
|
|
5
|
-
*/
|
|
6
|
-
declare const shardSequence: readonly ["C", "b", "A", "a", "B", "b", "C", "a", "A", "b", "B", "a"];
|
|
7
|
-
/**
|
|
8
|
-
* Sequence of realms pattern of shard
|
|
9
|
-
*/
|
|
10
|
-
declare const realmSequence: readonly ["prairie", "forest", "valley", "wasteland", "vault"];
|
|
11
|
-
/**
|
|
12
|
-
* @class shardsUtil
|
|
13
|
-
* @classdesc A class to handle shards and realms indexing.
|
|
14
|
-
*/
|
|
15
|
-
export declare class ShardsUtil {
|
|
16
|
-
/**
|
|
17
|
-
* @method getDate - get provided date in luxon
|
|
18
|
-
* @param date - date to get in moment
|
|
19
|
-
*/
|
|
20
|
-
static getDate(date?: string | undefined | null): DateTime | string;
|
|
21
|
-
/**
|
|
22
|
-
* Returns shards index for a given date
|
|
23
|
-
* @param date
|
|
24
|
-
*/
|
|
25
|
-
static shardsIndex(date: DateTime): {
|
|
26
|
-
currentShard: (typeof shardSequence)[number];
|
|
27
|
-
currentRealm: (typeof realmSequence)[number];
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Returns suffix for a given number
|
|
31
|
-
* @param number The number to get the suffix for
|
|
32
|
-
*/
|
|
33
|
-
static getSuffix(number: number): string;
|
|
34
|
-
/**
|
|
35
|
-
* Get all three shards status for a given date relative to the current time
|
|
36
|
-
* @param date The date for which to get the status for
|
|
37
|
-
*/
|
|
38
|
-
static getStatus(date: DateTime): ShardsCountdown[] | "No Shard";
|
|
39
|
-
}
|
|
40
|
-
export {};
|