Compare commits
9 commits
main
...
feature/48
Author | SHA1 | Date | |
---|---|---|---|
40a9764192 | |||
afafe246a8 | |||
94a2965e8e | |||
9e05d884cf | |||
8d1befcf25 | |||
2b4ae91584 | |||
bc3c16bdcb | |||
8e16a4fda6 | |||
bfbf386eeb |
19 changed files with 471 additions and 11 deletions
|
@ -7,7 +7,7 @@
|
|||
# any secret values.
|
||||
|
||||
BOT_TOKEN=
|
||||
BOT_VER=3.2.3
|
||||
BOT_VER=3.2.4
|
||||
BOT_AUTHOR=Vylpes
|
||||
BOT_OWNERID=147392775707426816
|
||||
BOT_CLIENTID=682942374040961060
|
||||
|
@ -23,4 +23,6 @@ DB_NAME=vylbot
|
|||
DB_AUTH_USER=dev
|
||||
DB_AUTH_PASS=dev
|
||||
DB_SYNC=true
|
||||
DB_LOGGING=true
|
||||
DB_LOGGING=true
|
||||
DB_DATA_LOCATION=./.temp/database
|
||||
DB_ROOT_HOST=0.0.0.0
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
- run: yarn test
|
||||
|
||||
- name: "Copy files over to location"
|
||||
run: rsync -rvzP . ${{ secrets.PROD_REPO_PATH }}
|
||||
run: cp -r . ${{ secrets.PROD_REPO_PATH }}
|
||||
|
||||
deploy:
|
||||
environment: prod
|
||||
|
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
- run: yarn test
|
||||
|
||||
- name: "Copy files over to location"
|
||||
run: rsync -rvzP . ${{ secrets.STAGE_REPO_PATH }}
|
||||
run: cp -r . ${{ secrets.STAGE_REPO_PATH }}
|
||||
|
||||
deploy:
|
||||
environment: prod
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE auto_kick_config;
|
|
@ -0,0 +1,10 @@
|
|||
CREATE TABLE auto_kick_config (
|
||||
Id varchar(255) NOT NULL,
|
||||
WhenCreated datetime NOT NULL,
|
||||
WhenUpdated datetime NOT NULL,
|
||||
ServerId varchar(255) NOT NULL,
|
||||
RoleId varchar(255) NOT NULL,
|
||||
KickTime int NOT NULL,
|
||||
NoticeTime int NULL,
|
||||
NoticeChannelId varchar(255) NULL
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE auto_kick_config
|
||||
ADD PRIMARY KEY (Id);
|
|
@ -30,6 +30,7 @@
|
|||
"@discordjs/rest": "^2.0.0",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"cron": "^3.3.1",
|
||||
"discord.js": "^14.3.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"emoji-regex": "^10.0.0",
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Client, Partials } from "discord.js";
|
||||
import * as dotenv from "dotenv";
|
||||
import { createConnection } from "typeorm";
|
||||
import { EventType } from "../constants/EventType";
|
||||
import ICommandItem from "../contracts/ICommandItem";
|
||||
import IEventItem from "../contracts/IEventItem";
|
||||
|
@ -12,14 +11,18 @@ import AppDataSource from "../database/dataSources/appDataSource";
|
|||
import ButtonEventItem from "../contracts/ButtonEventItem";
|
||||
import { ButtonEvent } from "../type/buttonEvent";
|
||||
import CacheHelper from "../helpers/CacheHelper";
|
||||
import TimerHelper from "../helpers/TimerHelper";
|
||||
import AutoKick from "../timers/AutoKick";
|
||||
|
||||
export class CoreClient extends Client {
|
||||
private static _commandItems: ICommandItem[];
|
||||
private static _eventItems: IEventItem[];
|
||||
private static _buttonEvents: ButtonEventItem[];
|
||||
private static _baseClient: Client;
|
||||
|
||||
private _events: Events;
|
||||
private _util: Util;
|
||||
private _timerHelper: TimerHelper;
|
||||
|
||||
public static get commandItems(): ICommandItem[] {
|
||||
return this._commandItems;
|
||||
|
@ -33,6 +36,10 @@ export class CoreClient extends Client {
|
|||
return this._buttonEvents;
|
||||
}
|
||||
|
||||
public static get baseClient(): Client {
|
||||
return this._baseClient;
|
||||
}
|
||||
|
||||
constructor(intents: number[], partials: Partials[]) {
|
||||
super({ intents: intents, partials: partials });
|
||||
dotenv.config();
|
||||
|
@ -43,6 +50,7 @@ export class CoreClient extends Client {
|
|||
|
||||
this._events = new Events();
|
||||
this._util = new Util();
|
||||
this._timerHelper = new TimerHelper();
|
||||
}
|
||||
|
||||
public async start() {
|
||||
|
@ -51,8 +59,16 @@ export class CoreClient extends Client {
|
|||
return;
|
||||
}
|
||||
|
||||
CoreClient._baseClient = this;
|
||||
|
||||
await AppDataSource.initialize()
|
||||
.then(() => console.log("Data Source Initialized"))
|
||||
.then(() => {
|
||||
console.log("Data Source Initialized");
|
||||
|
||||
this._timerHelper.AddTimer("0 * * * *", "Europe/London", AutoKick, true);
|
||||
|
||||
this._timerHelper.StartAllTimers();
|
||||
})
|
||||
.catch((err) => console.error("Error Initialising Data Source", err));
|
||||
|
||||
super.on("interactionCreate", this._events.onInteractionCreate);
|
||||
|
|
113
src/commands/autokick.ts
Normal file
113
src/commands/autokick.ts
Normal file
|
@ -0,0 +1,113 @@
|
|||
import {ChatInputCommandInteraction, CommandInteraction, EmbedBuilder, PermissionFlagsBits, SlashCommandBuilder} from "discord.js";
|
||||
import {Command} from "../type/command";
|
||||
import TimeLengthInput from "../helpers/TimeLengthInput";
|
||||
import AutoKickHelper from "../helpers/AutoKickHelper";
|
||||
|
||||
export default class Autokick extends Command {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.CommandBuilder = new SlashCommandBuilder()
|
||||
.setName("autokick")
|
||||
.setDescription("Configure the auto kick functionality")
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers)
|
||||
.addSubcommand(x => x
|
||||
.setName("set")
|
||||
.setDescription("Set the configuration")
|
||||
.addRoleOption(y => y
|
||||
.setName("role")
|
||||
.setDescription("The role the user needs to be auto kicked")
|
||||
.setRequired(true))
|
||||
.addStringOption(y => y
|
||||
.setName("kicktime")
|
||||
.setDescription("The time with the role before being kicked (Ex: 2h 30m)")
|
||||
.setRequired(true))
|
||||
.addStringOption(y => y
|
||||
.setName("noticetime")
|
||||
.setDescription("The time before being kicked when a notification is sent (Ex: 2h 30m)"))
|
||||
.addChannelOption(y => y
|
||||
.setName("noticechannel")
|
||||
.setDescription("The channel to send the notification to")))
|
||||
.addSubcommand(x => x
|
||||
.setName("unset")
|
||||
.setDescription("Unset the current configuration"));
|
||||
}
|
||||
|
||||
public override async execute(interaction: CommandInteraction) {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
switch (subcommand) {
|
||||
case "set":
|
||||
await this.set(interaction);
|
||||
break;
|
||||
case "unset":
|
||||
await this.unset(interaction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async set(interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guildId) return;
|
||||
|
||||
const roleOption = interaction.options.getRole("role", true);
|
||||
const kickTimeOption = interaction.options.getString("kicktime", true);
|
||||
const noticeTimeOption = interaction.options.getString("noticetime");
|
||||
const noticeChannelOption = interaction.options.getChannel("noticechannel");
|
||||
|
||||
const roleId = roleOption.id;
|
||||
const kickTimeInput = new TimeLengthInput(kickTimeOption);
|
||||
const noticeTimeInput = noticeTimeOption ? new TimeLengthInput(noticeTimeOption) : undefined;
|
||||
const noticeChannelId = noticeChannelOption?.id;
|
||||
|
||||
if ((noticeTimeInput && !noticeTimeOption) || (!noticeTimeInput && noticeChannelOption)) {
|
||||
await interaction.reply("Both `noticetime` and `noticechannel` must be set if you want a notification embed");
|
||||
return;
|
||||
}
|
||||
|
||||
await AutoKickHelper.SetSetting(interaction.guildId, roleId, kickTimeInput.GetMilliseconds(), noticeTimeInput?.GetMilliseconds(), noticeChannelId);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("Auto Kick")
|
||||
.setDescription("Configured auto kick for this server")
|
||||
.addFields([
|
||||
{
|
||||
name: "Role",
|
||||
value: roleOption.name,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "Kick Time",
|
||||
value: kickTimeInput.GetLengthShort(),
|
||||
inline: true,
|
||||
},
|
||||
]);
|
||||
|
||||
if (noticeTimeInput) {
|
||||
embed.addFields([
|
||||
{
|
||||
name: "Notice Time",
|
||||
value: noticeTimeInput.GetLengthShort(),
|
||||
},
|
||||
{
|
||||
name: "Notice Channel",
|
||||
value: noticeChannelOption!.name!,
|
||||
inline: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [ embed ],
|
||||
});
|
||||
}
|
||||
|
||||
private async unset(interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guildId) return;
|
||||
|
||||
await AutoKickHelper.UnsetSetting(interaction.guildId);
|
||||
|
||||
await interaction.reply("Unset the auto kick configuration for this server");
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
export default class EmbedColours {
|
||||
public static readonly Ok = 0x3050ba;
|
||||
public static readonly Warning = 0xffbf00;
|
||||
public static readonly Danger = 0xd2042d;
|
||||
}
|
61
src/database/entities/AutoKickConfig.ts
Normal file
61
src/database/entities/AutoKickConfig.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
import {Column, Entity} from "typeorm";
|
||||
import AppDataSource from "../dataSources/appDataSource";
|
||||
import BaseEntity from "../../contracts/BaseEntity";
|
||||
|
||||
@Entity()
|
||||
export default class AutoKickConfig extends BaseEntity {
|
||||
constructor(serverId: string, roleId: string, kickTime: number, noticeTime?: number, noticeChannelId?: string) {
|
||||
super();
|
||||
|
||||
this.ServerId = serverId;
|
||||
this.RoleId = roleId;
|
||||
this.KickTime = kickTime;
|
||||
this.NoticeTime = noticeTime;
|
||||
this.NoticeChannelId = noticeChannelId;
|
||||
}
|
||||
|
||||
@Column()
|
||||
ServerId: string;
|
||||
|
||||
@Column()
|
||||
RoleId: string;
|
||||
|
||||
@Column({ type: "int" })
|
||||
KickTime: number;
|
||||
|
||||
@Column({ type: "int", nullable: true })
|
||||
NoticeTime?: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
NoticeChannelId?: string;
|
||||
|
||||
public UpdateBasicDetails(roleId: string, kickTime: number, noticeTime?: number, noticeChannelId?: string) {
|
||||
this.RoleId = roleId;
|
||||
this.KickTime = kickTime;
|
||||
this.NoticeTime = noticeTime;
|
||||
this.NoticeChannelId = noticeChannelId;
|
||||
}
|
||||
|
||||
public static async FetchOneByServerIdAndRoleId(serverId: string, roleId: string): Promise<AutoKickConfig | null> {
|
||||
const repository = AppDataSource.getRepository(AutoKickConfig);
|
||||
|
||||
const query = repository
|
||||
.createQueryBuilder("config")
|
||||
.where("config.serverId = :serverId", { serverId })
|
||||
.andWhere("config.roleId = :roleId", { roleId })
|
||||
.getOne();
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
public static async FetchAllByServerId(serverId: string): Promise<AutoKickConfig[]> {
|
||||
const repository = AppDataSource.getRepository(AutoKickConfig);
|
||||
|
||||
const query = repository
|
||||
.createQueryBuilder("config")
|
||||
.where("config.serverId = :serverId", { serverId })
|
||||
.getMany();
|
||||
|
||||
return query;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
import MigrationHelper from "../../../helpers/MigrationHelper";
|
||||
|
||||
export class CreateAutoKickConfig1732973911304 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
MigrationHelper.Up("1732973911304-createAutoKickConfig", "3.2.4", [
|
||||
"01-AutoKickConfig-Table",
|
||||
"02-AutoKickConfig-Key",
|
||||
], queryRunner)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
MigrationHelper.Down("1732973911304-createAutoKickConfig", "3.2.4", [
|
||||
"01-AutoKickConfig",
|
||||
], queryRunner)
|
||||
}
|
||||
|
||||
}
|
|
@ -6,11 +6,7 @@ export default async function LinkOnlyMode(message: Message) {
|
|||
|
||||
const gifOnlyMode = await SettingsHelper.GetSetting("channel.linkonly", message.guild.id);
|
||||
|
||||
if (!gifOnlyMode) return;
|
||||
|
||||
const channel = message.guild.channels.cache.find(x => x.id == gifOnlyMode) || message.guild.channels.fetch(gifOnlyMode);
|
||||
|
||||
if (!channel) return;
|
||||
if (gifOnlyMode != message.channel.id) return;
|
||||
|
||||
if (message.content.startsWith("https://") || message.content.startsWith("http://")) return;
|
||||
|
||||
|
|
37
src/helpers/AutoKickHelper.ts
Normal file
37
src/helpers/AutoKickHelper.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import AutoKickConfig from "../database/entities/AutoKickConfig";
|
||||
|
||||
export default class AutoKickHelper {
|
||||
public static async GetSetting(serverId: string): Promise<AutoKickConfig | null> {
|
||||
const configs = await AutoKickConfig.FetchAllByServerId(serverId);
|
||||
|
||||
if (configs.length != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return configs[0];
|
||||
}
|
||||
|
||||
public static async SetSetting(serverId: string, roleId: string, kickTime: number, noticeTime?: number, noticeChannelId?: string) {
|
||||
const configs = await AutoKickConfig.FetchAllByServerId(serverId);
|
||||
|
||||
if (configs.length == 0) {
|
||||
const config = new AutoKickConfig(serverId, roleId, kickTime, noticeTime, noticeChannelId);
|
||||
await config.Save(AutoKickConfig, config);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const config = configs[0];
|
||||
|
||||
config.UpdateBasicDetails(roleId, kickTime, noticeTime, noticeChannelId);
|
||||
await config.Save(AutoKickConfig, config);
|
||||
}
|
||||
|
||||
public static async UnsetSetting(serverId: string) {
|
||||
const configs = await AutoKickConfig.FetchAllByServerId(serverId);
|
||||
|
||||
for (let config of configs) {
|
||||
await AutoKickConfig.Remove(AutoKickConfig, config);
|
||||
}
|
||||
}
|
||||
}
|
81
src/helpers/TimerHelper.ts
Normal file
81
src/helpers/TimerHelper.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
import {CronJob} from "cron";
|
||||
import {primitive} from "../type/primitive";
|
||||
import {v4} from "uuid";
|
||||
|
||||
interface Timer {
|
||||
id: string;
|
||||
job: CronJob;
|
||||
context: Map<string, primitive>;
|
||||
onTick: ((context: Map<string, primitive>) => void) | ((context: Map<string, primitive>) => Promise<void>);
|
||||
runOnStart: boolean;
|
||||
}
|
||||
|
||||
export default class TimerHelper {
|
||||
private _timers: Timer[];
|
||||
|
||||
constructor() {
|
||||
this._timers = [];
|
||||
}
|
||||
|
||||
public AddTimer(
|
||||
cronTime: string,
|
||||
timeZone: string,
|
||||
onTick: ((context: Map<string, primitive>) => void) | ((context: Map<string, primitive>) => Promise<void>),
|
||||
runOnStart: boolean = false): string {
|
||||
const context = new Map<string, primitive>();
|
||||
|
||||
const job = new CronJob(
|
||||
cronTime,
|
||||
() => {
|
||||
onTick(context);
|
||||
},
|
||||
null,
|
||||
false,
|
||||
timeZone,
|
||||
);
|
||||
|
||||
const id = v4();
|
||||
|
||||
this._timers.push({
|
||||
id,
|
||||
job,
|
||||
context,
|
||||
onTick,
|
||||
runOnStart,
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public StartAllTimers() {
|
||||
this._timers.forEach(timer => this.StartJob(timer));
|
||||
}
|
||||
|
||||
public StopAllTimers() {
|
||||
this._timers.forEach(timer => timer.job.stop());
|
||||
}
|
||||
|
||||
public StartTimer(id: string) {
|
||||
const timer = this._timers.find(x => x.id == id);
|
||||
|
||||
if (!timer) return;
|
||||
|
||||
this.StartJob(timer);
|
||||
}
|
||||
|
||||
public StopTimer(id: string) {
|
||||
const timer = this._timers.find(x => x.id == id);
|
||||
|
||||
if (!timer) return;
|
||||
|
||||
timer.job.stop();
|
||||
}
|
||||
|
||||
private StartJob(timer: Timer) {
|
||||
timer.job.start();
|
||||
|
||||
if (timer.runOnStart) {
|
||||
timer.onTick(timer.context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import { EventType } from "./constants/EventType";
|
|||
// Command Imports
|
||||
import About from "./commands/about";
|
||||
import Audits from "./commands/audits";
|
||||
import Autokick from "./commands/autokick";
|
||||
import Ban from "./commands/ban";
|
||||
import Bunny from "./commands/bunny";
|
||||
import Clear from "./commands/clear";
|
||||
|
@ -45,6 +46,7 @@ export default class Registry {
|
|||
public static RegisterCommands() {
|
||||
CoreClient.RegisterCommand("about", new About());
|
||||
CoreClient.RegisterCommand("audits", new Audits());
|
||||
CoreClient.RegisterCommand("autokick", new Autokick());
|
||||
CoreClient.RegisterCommand("ban", new Ban());
|
||||
CoreClient.RegisterCommand("bunny", new Bunny());
|
||||
CoreClient.RegisterCommand("clear", new Clear());
|
||||
|
|
98
src/timers/AutoKick.ts
Normal file
98
src/timers/AutoKick.ts
Normal file
|
@ -0,0 +1,98 @@
|
|||
import { EmbedBuilder } from "discord.js";
|
||||
import {CoreClient} from "../client/client";
|
||||
import AutoKickConfig from "../database/entities/AutoKickConfig";
|
||||
import EmbedColours from "../constants/EmbedColours";
|
||||
|
||||
export default async function AutoKick() {
|
||||
const client = CoreClient.baseClient;
|
||||
const autoKickConfigs = await AutoKickConfig.FetchAll(AutoKickConfig);
|
||||
|
||||
for (let config of autoKickConfigs) {
|
||||
const guild = client.guilds.cache.find(x => x.id == config.ServerId) || await client.guilds.fetch(config.ServerId);
|
||||
|
||||
if (!guild) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await guild.members.fetch();
|
||||
|
||||
const role = guild.roles.cache.find(x => x.id == config.RoleId);
|
||||
|
||||
if (!role) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let memberEntity of role.members) {
|
||||
const member = memberEntity[1];
|
||||
|
||||
if (!member.kickable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const whenToKick = new Date(member.joinedTimestamp! + config.KickTime);
|
||||
const now = new Date();
|
||||
|
||||
if (whenToKick < now) {
|
||||
// await member.kick("Auto Kicked");
|
||||
console.log("Kicked");
|
||||
|
||||
if (config.NoticeChannelId) {
|
||||
const channel = guild.channels.cache.find(x => x.id == config.NoticeChannelId) || await guild.channels.fetch(config.NoticeChannelId);
|
||||
|
||||
if (!channel?.isSendable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("Auto Kicked User")
|
||||
.setColor(EmbedColours.Danger)
|
||||
.setThumbnail(member.avatarURL())
|
||||
.addFields([
|
||||
{
|
||||
name: "User",
|
||||
value: `<@${member.user.id}> \`${member.user.username}\``,
|
||||
inline: true,
|
||||
},
|
||||
]);
|
||||
|
||||
await channel.send({
|
||||
embeds: [ embed ],
|
||||
});
|
||||
}
|
||||
} else if (config.NoticeChannelId && config.NoticeTime) {
|
||||
const whenToNotice = new Date(whenToKick.getMilliseconds() - config.NoticeTime);
|
||||
|
||||
const channel = guild.channels.cache.find(x => x.id == config.NoticeChannelId) || await guild.channels.fetch(config.NoticeChannelId);
|
||||
|
||||
if (!channel?.isSendable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (now.getMonth() == whenToNotice.getMonth()
|
||||
&& now.getDate() == whenToNotice.getDate()
|
||||
&& now.getHours() == whenToNotice.getHours()) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("Auto Kick Notice")
|
||||
.setColor(EmbedColours.Warning)
|
||||
.setThumbnail(member.avatarURL())
|
||||
.addFields([
|
||||
{
|
||||
name: "User",
|
||||
value: `<@${member.user.id}> \`${member.user.username}\``,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "When To Kick",
|
||||
value: `<t:${whenToKick.getMilliseconds()}:R>`,
|
||||
inline: true,
|
||||
},
|
||||
]);
|
||||
|
||||
await channel.send({
|
||||
embeds: [ embed ],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
src/type/primitive.ts
Normal file
1
src/type/primitive.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export type primitive = string | number | boolean;
|
18
yarn.lock
18
yarn.lock
|
@ -788,6 +788,11 @@
|
|||
expect "^29.0.0"
|
||||
pretty-format "^29.0.0"
|
||||
|
||||
"@types/luxon@~3.4.0":
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7"
|
||||
integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==
|
||||
|
||||
"@types/node@*":
|
||||
version "22.7.5"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz"
|
||||
|
@ -1458,6 +1463,14 @@ create-jest@^29.7.0:
|
|||
jest-util "^29.7.0"
|
||||
prompts "^2.0.1"
|
||||
|
||||
cron@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/cron/-/cron-3.3.1.tgz#03c56b4a3ad52606160adfba1fab932c53838807"
|
||||
integrity sha512-KpvuzJEbeTMTfLsXhUuDfsFYr8s5roUlLKb4fa68GszWrA4783C7q6m9yj4vyc6neyD/V9e0YiADSX2c+yRDXg==
|
||||
dependencies:
|
||||
"@types/luxon" "~3.4.0"
|
||||
luxon "~3.5.0"
|
||||
|
||||
cross-spawn@^7.0.0, cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
|
||||
|
@ -3076,6 +3089,11 @@ lru-cache@^5.1.1:
|
|||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
luxon@~3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20"
|
||||
integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==
|
||||
|
||||
magic-bytes.js@^1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz"
|
||||
|
|
Loading…
Reference in a new issue