@skyhelperbot/utils 2.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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/dist/classes/LeaderBoardCard.d.ts +59 -0
  4. package/dist/classes/LeaderBoardCard.js +249 -0
  5. package/dist/classes/SkytimesUtils.d.ts +73 -0
  6. package/dist/classes/SkytimesUtils.js +119 -0
  7. package/dist/classes/UpdateEvent.d.ts +30 -0
  8. package/dist/classes/UpdateEvent.js +52 -0
  9. package/dist/classes/UpdateTs.d.ts +35 -0
  10. package/dist/classes/UpdateTs.js +62 -0
  11. package/dist/classes/WinnerCard.d.ts +13 -0
  12. package/dist/classes/WinnerCard.js +118 -0
  13. package/dist/classes/index.d.ts +7 -0
  14. package/dist/classes/index.js +7 -0
  15. package/dist/classes/shardsUtil.d.ts +41 -0
  16. package/dist/classes/shardsUtil.js +122 -0
  17. package/dist/classes/utils.d.ts +13 -0
  18. package/dist/classes/utils.js +22 -0
  19. package/dist/constants/eventDatas.d.ts +31 -0
  20. package/dist/constants/eventDatas.js +105 -0
  21. package/dist/constants/index.d.ts +5 -0
  22. package/dist/constants/index.js +5 -0
  23. package/dist/constants/shardsInfo.d.ts +50 -0
  24. package/dist/constants/shardsInfo.js +424 -0
  25. package/dist/constants/shardsTimeline.d.ts +19 -0
  26. package/dist/constants/shardsTimeline.js +57 -0
  27. package/dist/index.d.ts +6 -0
  28. package/dist/index.js +5 -0
  29. package/dist/typings.d.ts +62 -0
  30. package/dist/typings.js +3 -0
  31. package/dist/utils/buildTimesHTML.d.ts +14 -0
  32. package/dist/utils/buildTimesHTML.js +208 -0
  33. package/dist/utils/index.d.ts +5 -0
  34. package/dist/utils/index.js +5 -0
  35. package/dist/utils/parseDateFormat.d.ts +9 -0
  36. package/dist/utils/parseDateFormat.js +25 -0
  37. package/dist/utils/parsePerms.d.ts +49 -0
  38. package/dist/utils/parsePerms.js +54 -0
  39. package/dist/utils/postToBin.d.ts +5 -0
  40. package/dist/utils/postToBin.js +15 -0
  41. package/dist/utils/recursiveReadDir.d.ts +15 -0
  42. package/dist/utils/recursiveReadDir.js +37 -0
  43. package/package.json +44 -0
  44. package/shared/assets/Point.png +0 -0
  45. package/shared/assets/Win.png +0 -0
  46. package/shared/assets/medal_champion_award_winner_olympic_icon_207790.png +0 -0
  47. package/shared/assets/server.svg +41 -0
  48. package/shared/fonts/circularstd-black.otf +0 -0
  49. package/shared/fonts/notoemoji-bold.ttf +0 -0
  50. package/shared/fonts/notosans-black.ttf +0 -0
  51. package/shared/fonts/notosans-jp-black.ttf +0 -0
  52. package/shared/fonts/notosans-kr-black.ttf +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Naiyar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+
2
+ ### <p align="center"> Utility Package for <a href="https://github.com/imnaiyar/skyhelper">SkyHelper</a> bot.</p>
3
+
4
+ This package contains various utilities for SkyHelper discord bot!
5
+
6
+ Run this to install
7
+ ```
8
+ npm install skyhelper-utils
9
+ ```
10
+
11
+ **Documentation**: https://utils.skyhelper.xyz/
@@ -0,0 +1,59 @@
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
+ }
@@ -0,0 +1,249 @@
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(__dirname, `../../shared/fonts/circularstd-black.otf`), "circular-std");
122
+ GlobalFonts.registerFromPath(path.join(__dirname, `../../shared/fonts/notosans-jp-black.ttf`), "noto-sans-jp");
123
+ GlobalFonts.registerFromPath(path.join(__dirname, `../../shared/fonts/notosans-black.ttf`), "noto-sans");
124
+ GlobalFonts.registerFromPath(path.join(__dirname, `../../shared/fonts/notoemoji-bold.ttf`), "noto-emoji");
125
+ GlobalFonts.registerFromPath(path.join(__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
+ }
@@ -0,0 +1,73 @@
1
+ import moment from "moment-timezone";
2
+ import "moment-duration-format";
3
+ import { eventData } from "../constants/eventDatas.js";
4
+ type EventKey = keyof typeof eventData;
5
+ /**
6
+ * Utilities for skytimes
7
+ */
8
+ export declare class SkytimesUtils {
9
+ /** Get all occurences of an event for the given date
10
+ * @param eventTime The moment date for which to get all occurences
11
+ * @param interval The interval between the event occurence's
12
+ */
13
+ static getAllTimes(eventTime: moment.Moment, interval?: number): string;
14
+ /**
15
+ * Get the date in moment on which the event will occur (if it's not a daily event)
16
+ * @param event The event for which to get the date-time
17
+ */
18
+ private static getOccurenceDay;
19
+ /**
20
+ * Get the next occurence of the event relative to now
21
+ * @param eventName The key of the event
22
+ */
23
+ private static getNextEventOccurrence;
24
+ /**
25
+ * Get the details about an event, their status, next occurence, al occurences for the day
26
+ * @param key The event key
27
+ */
28
+ static getEventDetails(key: EventKey): EventDetails;
29
+ /**
30
+ * Same as {@apilink SkytimesUtils.getEventDetails | getEventDetails} but for all of the events
31
+ * @returns An array of event details
32
+ */
33
+ static allEventDetails(): [EventKey, EventDetails][];
34
+ /**
35
+ * Returns the event status of a given event
36
+ * @param event The event to get the status for
37
+ * @param nextOccurence The next occurence of the event relative to "now"
38
+ * @returns The event status (or null if there is no active duration)
39
+ */
40
+ static getEventStatus(event: (typeof eventData)[keyof typeof eventData], nextOccurence: moment.Moment): Times;
41
+ }
42
+ export interface BaseTimes {
43
+ /** Whether the event is active or not */
44
+ active: boolean;
45
+ /** The time when the event starts */
46
+ nextTime: moment.Moment;
47
+ /** This will be the countdown for when the event ends if it's active,
48
+ * otherwise it'll be the countdown to the next occurence
49
+ */
50
+ duration: string;
51
+ }
52
+ export interface ActiveTimes extends BaseTimes {
53
+ active: true;
54
+ /** The time when the event started if active */
55
+ startTime: moment.Moment;
56
+ /** The time when the event ends if active */
57
+ endTime: moment.Moment;
58
+ }
59
+ export interface NotActiveTimes extends BaseTimes {
60
+ active: false;
61
+ /** The time when the event started if active */
62
+ startTime?: moment.Moment;
63
+ /** The time when the event ends if active */
64
+ endTime?: moment.Moment;
65
+ }
66
+ export type Times = ActiveTimes | NotActiveTimes;
67
+ export interface EventDetails {
68
+ event: (typeof eventData)[EventKey];
69
+ nextOccurence: moment.Moment;
70
+ allOccurences: string;
71
+ status: Times;
72
+ }
73
+ export {};
@@ -0,0 +1,119 @@
1
+ import { Base, time } from "discord.js";
2
+ import moment from "moment-timezone";
3
+ import "moment-duration-format";
4
+ import { eventData } from "../constants/eventDatas.js";
5
+ /**
6
+ * Utilities for skytimes
7
+ */
8
+ export class SkytimesUtils {
9
+ /** Get all occurences of an event for the given date
10
+ * @param eventTime The moment date for which to get all occurences
11
+ * @param interval The interval between the event occurence's
12
+ */
13
+ static getAllTimes(eventTime, interval) {
14
+ const clonedTime = eventTime.clone();
15
+ const timeBuilt = [];
16
+ while (eventTime.date() === clonedTime.date()) {
17
+ timeBuilt.push(time(clonedTime.toDate(), "t"));
18
+ clonedTime.add(interval || 0, "minutes");
19
+ }
20
+ return timeBuilt.join(" • ");
21
+ }
22
+ /**
23
+ * Get the date in moment on which the event will occur (if it's not a daily event)
24
+ * @param event The event for which to get the date-time
25
+ */
26
+ static getOccurenceDay(event) {
27
+ const nextOccurrence = moment().tz("America/Los_Angeles").startOf("day").add(event.offset, "minutes"); // Start with the offset from the beginning of the day
28
+ if (event.occursOn) {
29
+ // If the event occurs on specific weekdays
30
+ if (event.occursOn.weekDays) {
31
+ while (!event.occursOn.weekDays.includes(nextOccurrence.isoWeekday())) {
32
+ nextOccurrence.add(1, "day");
33
+ }
34
+ }
35
+ // If the event occurs on a specific day of the month
36
+ if (event.occursOn.dayOfTheMonth) {
37
+ while (nextOccurrence.date() !== event.occursOn.dayOfTheMonth) {
38
+ nextOccurrence.add(1, "day");
39
+ }
40
+ }
41
+ }
42
+ return nextOccurrence;
43
+ }
44
+ /**
45
+ * Get the next occurence of the event relative to now
46
+ * @param eventName The key of the event
47
+ */
48
+ static getNextEventOccurrence(eventName) {
49
+ const event = eventData[eventName];
50
+ if (!event)
51
+ throw new Error("Unknow Event");
52
+ const now = moment().tz("America/Los_Angeles"); // Current time
53
+ const nextOccurrence = this.getOccurenceDay(event);
54
+ // Loop to calculate the next occurrence based on the interval
55
+ while (nextOccurrence.isBefore(now)) {
56
+ nextOccurrence.add(event.interval || 0, "minutes");
57
+ }
58
+ return nextOccurrence;
59
+ }
60
+ /**
61
+ * Get the details about an event, their status, next occurence, al occurences for the day
62
+ * @param key The event key
63
+ */
64
+ static getEventDetails(key) {
65
+ const nextOccurence = this.getNextEventOccurrence(key);
66
+ return {
67
+ event: eventData[key],
68
+ nextOccurence,
69
+ allOccurences: this.getAllTimes(this.getOccurenceDay(eventData[key]), eventData[key].interval),
70
+ status: this.getEventStatus(eventData[key], nextOccurence),
71
+ };
72
+ }
73
+ /**
74
+ * Same as {@apilink SkytimesUtils.getEventDetails | getEventDetails} but for all of the events
75
+ * @returns An array of event details
76
+ */
77
+ static allEventDetails() {
78
+ const keys = Object.keys(eventData).sort((a, b) => eventData[a].index - eventData[b].index);
79
+ const occurences = [];
80
+ for (const key of keys) {
81
+ occurences.push([key, this.getEventDetails(key)]);
82
+ }
83
+ return occurences;
84
+ }
85
+ /**
86
+ * Returns the event status of a given event
87
+ * @param event The event to get the status for
88
+ * @param nextOccurence The next occurence of the event relative to "now"
89
+ * @returns The event status (or null if there is no active duration)
90
+ */
91
+ static getEventStatus(event, nextOccurence) {
92
+ const now = moment().tz("America/Los_Angeles");
93
+ const BASE = {
94
+ active: false,
95
+ nextTime: nextOccurence,
96
+ duration: moment.duration(nextOccurence.diff(now)).format("d[d] h[h] m[m] s[s]"),
97
+ };
98
+ if (!event.duration)
99
+ return BASE;
100
+ // Substract the interval because nextOccurence always calculates the next upcoming event
101
+ // So we substract the interval to get the last occurence, and add the active duration to it, and check if now is between those
102
+ const start = nextOccurence.clone().subtract(event.interval || 0, "minutes");
103
+ const end = start.clone().add(event.duration, "minutes");
104
+ // When active
105
+ if (now.isBetween(start, end)) {
106
+ return {
107
+ active: true,
108
+ startTime: start,
109
+ endTime: end,
110
+ // TODO: This maybe not needed as nextoccurence is inncluded in original object returned from `eventOccurence()`
111
+ nextTime: nextOccurence,
112
+ duration: moment.duration(end.diff(now)).format("d[d] h[h] m[m] s[s]"),
113
+ };
114
+ }
115
+ else {
116
+ return BASE;
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,30 @@
1
+ import type { SpecialEventData } from "../typings.js";
2
+ /**
3
+ * @class
4
+ * @classdesc A class to update Events details in the client constructor
5
+ * @method update Updates the event details
6
+ */
7
+ export declare class UpdateEvent {
8
+ readonly data: SpecialEventData;
9
+ constructor(data: SpecialEventData);
10
+ /**
11
+ * @param name Name of the event
12
+ */
13
+ setName(name: string): this;
14
+ /**
15
+ * @param date Start date of the Event. Format DD-MM-YYYY
16
+ * @example
17
+ * new UpdateEvent().setDate('22-09-2023')
18
+ */
19
+ setStart(date: string): this;
20
+ /**
21
+ * @param date End date of the Event. Format DD-MM-YYYY
22
+ * @example
23
+ * new UpdateEvent().setDate('22-09-2023')
24
+ */
25
+ setEnd(date: string): this;
26
+ /**
27
+ * @returns The updated event
28
+ */
29
+ update(): Promise<SpecialEventData>;
30
+ }
@@ -0,0 +1,52 @@
1
+ import moment from "moment-timezone";
2
+ /**
3
+ * @class
4
+ * @classdesc A class to update Events details in the client constructor
5
+ * @method update Updates the event details
6
+ */
7
+ export class UpdateEvent {
8
+ data;
9
+ constructor(data) {
10
+ this.data = data;
11
+ }
12
+ /**
13
+ * @param name Name of the event
14
+ */
15
+ setName(name) {
16
+ if (!name || typeof name !== "string") {
17
+ throw new TypeError("Name must be a non-empty string.");
18
+ }
19
+ this.data.name = name;
20
+ return this;
21
+ }
22
+ /**
23
+ * @param date Start date of the Event. Format DD-MM-YYYY
24
+ * @example
25
+ * new UpdateEvent().setDate('22-09-2023')
26
+ */
27
+ setStart(date) {
28
+ if (!date || typeof date !== "string") {
29
+ throw new TypeError("Date must be a non-empty string.");
30
+ }
31
+ this.data.startDate = date;
32
+ return this;
33
+ }
34
+ /**
35
+ * @param date End date of the Event. Format DD-MM-YYYY
36
+ * @example
37
+ * new UpdateEvent().setDate('22-09-2023')
38
+ */
39
+ setEnd(date) {
40
+ if (!date || typeof date !== "string") {
41
+ throw new TypeError("Date must be a non-empty string.");
42
+ }
43
+ this.data.endDate = date;
44
+ return this;
45
+ }
46
+ /**
47
+ * @returns The updated event
48
+ */
49
+ async update() {
50
+ return await this.data.save();
51
+ }
52
+ }
@@ -0,0 +1,35 @@
1
+ import type { TSData } from "../typings.js";
2
+ /**
3
+ * @class
4
+ * @classdesc A class to update traveling spirit details in the client constructor
5
+ * @method update updates the ts details
6
+ * @returns {Object}
7
+ */
8
+ export declare class UpdateTS {
9
+ readonly data: TSData;
10
+ constructor(data: TSData);
11
+ /**
12
+ * Sets the name of the TS
13
+ * @param name Name of the returning TS
14
+ */
15
+ setName(name: string): this;
16
+ /**
17
+ * Sets the visit date of the ts
18
+ * @param date Returnig date. Format: DD-MM-YYYY
19
+ */
20
+ setVisit(date: string): this;
21
+ /**
22
+ * Sets the value of the t spirit
23
+ * @param value The value of the spirit in the spiritsData
24
+ */
25
+ setValue(value: string): this;
26
+ /**
27
+ * Sets the index of the returning ts
28
+ * @param index The returning index of the TS
29
+ */
30
+ setIndex(index: number): this;
31
+ /**
32
+ * returns the updated ts details
33
+ */
34
+ update(): Promise<TSData>;
35
+ }