feat: Write4, moving to version 4, giveaway command (#46)

* fix compile errors

* v4

* giveaway progress

* forgot to fix warning

* 15+ minute should work

* giveaway command functional

* should now exclude host

* date-fns + host excluded

* minor fixes + more exclude host

---------

Co-authored-by: kingomes <83099848+kingomes@users.noreply.github.com>
This commit is contained in:
Jacob Nguyen
2024-09-01 02:54:48 -05:00
committed by GitHub
parent e11d4e1070
commit 5bb910dd3c
32 changed files with 779 additions and 414 deletions

View File

@@ -28,7 +28,9 @@
"license": "MIT",
"dependencies": {
"@octokit/rest": "19.0.7",
"@sern/handler": "^3.3.2",
"@sern/handler": "^4.0.0",
"better-sqlite3": "^11.1.2",
"date-fns": "^3.6.0",
"discord.js": "^14.14.1",
"dotenv": "16.0.3",
"jsdoc-parse-plus": "1.3.0",

View File

@@ -19,6 +19,7 @@ function handleComments(sum: PurpleSummary) {
}
const docHandler = new DocHandler();
docHandler.setup();
export default commandModule({
type: CommandType.Slash,
description: "Query documentation",
@@ -43,8 +44,8 @@ export default commandModule({
},
},
],
execute: async (context, options) => {
const option = options[1].getString("search", true);
execute: async (context) => {
const option = context.options.getString("search", true);
const result = docHandler.DocTrie.search(option);
if (!result.length) {

View File

@@ -44,15 +44,15 @@ export default slashCommand({
],
},
],
execute: async (ctx, [, args]) => {
const command = args.getSubcommand();
execute: async (ctx) => {
const command = ctx.options.getSubcommand();
await ctx.interaction.deferReply();
switch (command) {
case "submit": {
const attachment = args.getAttachment("attachment");
const urlString = args.getString("url");
const name = args.getString("name", true);
const attachment = ctx.options.getAttachment("attachment");
const urlString = ctx.options.getString("url");
const name = ctx.options.getString("name", true);
const send = sendTo("1014582281907753080", ctx.member as GuildMember, name);
if (attachment) {
@@ -60,11 +60,8 @@ export default slashCommand({
attachment,
(a) => a.size <= 256_000,
(a) => a.contentType?.startsWith("image/") || false,
(a) =>
["image/png", "image/jpg", "image/gif"].includes(
a.contentType ??
"Something that is not png or jpg when contentType is null",
),
(a) => ["image/png", "image/jpg", "image/gif"].includes(
a.contentType ?? "Something that is not png or jpg when contentType is null"),
);
if (!isValidAttachment) {
return ctx.interaction.editReply({

View File

@@ -6,11 +6,10 @@ import { Evo, Seren, Mina } from "#constants";
export default commandModule({
type: CommandType.Text,
description: "Eval something",
alias: ["ev"],
execute: async (ctx, args) => {
execute: async (ctx) => {
if (![Evo, Seren, Mina].includes(ctx.user.id)) return;
let code: string[] | string = args[1];
let code: string[] | string = ctx.options;
code = code.join(" ") as string;
if (code.includes("await")) {
@@ -45,27 +44,7 @@ export default commandModule({
if ((result as string).length > 2000) {
channel!.send("Result is too long to send");
}
ctx.channel!.send({ content: result as string });
function send(id: string, ping = false) {
const channel = client.channels.cache.get(id);
if (!channel) return;
const embed = new EmbedBuilder()
.setColor(0xcc5279)
.setTitle("v2 is out!")
.setThumbnail(client.user?.displayAvatarURL() ?? "")
.setImage("https://raw.githubusercontent.com/sern-handler/.github/main/banner.png")
.setAuthor({ name: "sern", url: "https://sern.dev/" })
.setDescription(
`__**Quick Look:**__\n\n${text()}\n\nThank you all for being patient!`,
)
.setFooter({ text: "Supports DJS v14.7 and above" })
.setTimestamp();
const content = ping ? "@everyone" : undefined;
channel.isTextBased() && channel.send({ content, embeds: [embed] });
return "Done sir";
}
},
});

View File

@@ -1,10 +1,10 @@
import { publish } from "#plugins";
import { CommandType, Service, commandModule } from "@sern/handler";
import { CommandType, commandModule } from "@sern/handler";
import { ApplicationCommandOptionType } from "discord.js";
import { Timestamp } from "#utils";
import { Emojis } from "#constants";
const prefix = (t: unknown) => (t ? "$" : "#");
const octokit = Service("octokit");
export default commandModule({
type: CommandType.Slash,
description: "Get info about a PR or issue",
@@ -18,7 +18,7 @@ export default commandModule({
autocomplete: true,
command: {
onEvent: [],
async execute(ctx) {
async execute(ctx, { deps: { octokit } }) {
const text = ctx.options.getFocused();
const org = await octokit.repos.listForOrg({
org: "sern-handler",
@@ -32,10 +32,8 @@ export default commandModule({
const publicRepos = topRepos
.filter((r) => !r.private)
.map((repo) => ({
name: `sern/${repo.name}`,
value: repo.name,
}));
.map((repo) => ({ name: `sern/${repo.name}`,
value: repo.name }));
if (!text.length) {
return ctx.respond(publicRepos.slice(0, 25)).catch(() => null);
@@ -60,7 +58,7 @@ export default commandModule({
autocomplete: true,
command: {
onEvent: [],
async execute(ctx) {
async execute(ctx, { deps }) {
const text = ctx.options.getFocused();
const repo = ctx.options.getString("repo");
if (!repo) return ctx.respond([]);
@@ -68,7 +66,7 @@ export default commandModule({
let search;
if (text.length) {
search = await octokit.search
search = await deps.octokit.search
.issuesAndPullRequests({
q: `repo:sern-handler/${repo} ${text} in:title`,
})
@@ -76,7 +74,7 @@ export default commandModule({
}
if (!text.length) {
const issues = await octokit.issues
const issues = await deps.octokit.issues
.listForRepo({
owner: "sern-handler",
repo,
@@ -120,10 +118,10 @@ export default commandModule({
required: false,
},
],
async execute(ctx, [, options]) {
const repo = options.getString("repo", true);
const number = options.getInteger("number", true);
const target = options.getUser("target");
async execute(ctx, { deps: { octokit }}) {
const repo = ctx.options.getString("repo", true);
const number = ctx.options.getInteger("number", true);
const target = ctx.options.getUser("target");
const issue = await octokit.issues
.get({

165
src/commands/giveaway.ts Normal file
View File

@@ -0,0 +1,165 @@
import { commandModule, CommandType, scheduledTask } from "@sern/handler";
import { publish } from "#plugins";
import { ApplicationCommandOptionType, EmbedBuilder } from "discord.js";
import { db } from "../utils/db.js";
import { add, addDays, addHours, addMinutes, addSeconds } from "date-fns"
import { Timestamp } from "#utils";
export default commandModule({
type: CommandType.Slash,
description: "Start a giveaway involving users who react to the embed",
plugins: [publish()],
options: [
{
name: "item",
description: "The item that will be given away",
type: ApplicationCommandOptionType.String,
required: true
},
{
name: "time",
description: "The amount of time that the giveaway will be up",
type: ApplicationCommandOptionType.String,
required: true
}
],
execute: async (ctx, { deps }) => {
const item = ctx.options.getString("item")
const timeLeftString = ctx.options.getString("time")
let timeUnit1
let timeLeft1
let timeUnit2
let timeLeft2
const [part1, part2] = timeLeftString?.split("and")
timeUnit1 = part1?.split(" ")[1]
timeLeft1 = Number(part1?.split(" ")[0])
if (part2) {
const timeLeftStringPart2 = part2.replace(part2.substring(0, 1), "")
timeUnit2 = timeLeftStringPart2?.split(" ")[1]
timeLeft2 = Number(timeLeftStringPart2?.split(" ")[0])
}
const startTime = new Date()
let endTime: Date
const secondNames = ['seconds', 'second', 'sec', 'secs']
const minuteNames = ['minutes', 'minute', 'min', 'mins']
const hourNames = ['hours', 'hour', 'hr', 'hrs']
const dayNames = ['days', 'day']
endTime = add(startTime, {
timeUnit1: timeLeft1,
timeUnit2: timeLeft2
})
// This if chain uses date-fns to correctly calculate the time allocated to the giveaway based on what the
// user types (seconds, minutes, etc.)
// if the time unit before the "and" is "seconds" or one of the other entries in the secondNames array, add the time entered
// to the startTime and save that in the endTime
if (secondNames.includes(timeUnit1!)) {
endTime = endTime === startTime ? addSeconds(startTime, timeLeft1) : addSeconds(endTime, timeLeft1)
}
// if the time unit after the "and" is "seconds" or one of the other entries in the secondNames array, add the time entered
// to the startTime and save that in the endTime
if (secondNames.includes(timeUnit2!)) {
endTime = endTime === startTime ? addSeconds(startTime, timeLeft2!) : addSeconds(endTime, timeLeft2!)
}
// if the time unit before the "and" is "minutes" or one of the other entries in the minuteNames array, add the time entered
// to the startTime and save that in the endTime
if (minuteNames.includes(timeUnit1!)) {
endTime = endTime === startTime ? addMinutes(startTime, timeLeft1) : addMinutes(endTime, timeLeft1)
}
// if the time unit after the "and" is "minutes" or one of the other entries in the minuteNames array, add the time entered
// to the startTime and save that in the endTime
if (minuteNames.includes(timeUnit2!)) {
endTime = endTime === startTime ? addMinutes(startTime, timeLeft2!) : addMinutes(endTime, timeLeft2!)
}
// if the time unit before the "and" is "hours" or one of the other entries in the hourNames array, add the time entered
// to the startTime and save that in the endTime
if (hourNames.includes(timeUnit1!)) {
endTime = endTime === startTime ? addHours(startTime, timeLeft1) : addHours(endTime, timeLeft1)
}
// if the time unit after the "and" is "hours" or one of the other entries in the hourNames array, add the time entered
// to the startTime and save that in the endTime
if (hourNames.includes(timeUnit2!)) {
endTime = endTime === startTime ? addHours(startTime, timeLeft2!) : addHours(endTime, timeLeft2!)
}
// if the time unit before the "and" is "days" or one of the other entries in the dayNames array, add the time entered
// to the startTime and save that in the endTime
if (dayNames.includes(timeUnit1!)) {
endTime = endTime === startTime ? addDays(startTime, timeLeft1) : addDays(endTime, timeLeft1)
}
// if the time unit after the "and" is "days" or one of the other entries in the dayNames array, add the time entered
// to the startTime and save that in the endTime
if (dayNames.includes(timeUnit2!)) {
endTime = endTime === startTime ? addDays(startTime, timeLeft2!) : addDays(endTime, timeLeft2!)
}
const endTimeStamp: string = `<t:${Math.floor(endTime!.getTime() / 1000)}:f>`
const endTimeStamp2 = new Timestamp(endTime.getTime()).timestamp
let embed = new EmbedBuilder()
.setTitle(`🥳 ${item} giveaway 🥳`)
.setDescription('React to enter the giveaway!')
.addFields(
{name: '\u200b', value: `Hosted by: <@${ctx.userId}>`},
{name: '\u200b', value: `Ends: ${new Timestamp(Number(endTimeStamp2)).getRelativeTime()} (${endTimeStamp})`}
)
await ctx.reply({
embeds: [embed],
}).then(embedMessage => {
embedMessage.react("🎉")
setInterval(() => {
const userReactions = embedMessage.reactions.cache.filter(reaction => reaction.users.cache.has(ctx.userId));
for (const reaction of userReactions.values()) {
reaction.users.remove(ctx.userId);
}
}, 1000)
let intervalTime = endTime.getTime() - startTime.getTime()
setTimeout(() => {
const stmt = db.prepare(`SELECT * FROM entrees`).all()
let winnerIndex = Math.floor(Math.random() * stmt.length)
if (stmt.length > 0 && stmt[winnerIndex].user_id !== ctx.userId) {
const winnerId = stmt[winnerIndex].user_id
embed.setDescription('\u200b')
embed.setFields(
{name: '\u200b', value: `Winner: <@${winnerId}>`},
{name: '\u200b', value: `Hosted by: <@${ctx.userId}>`},
{name: '\u200b', value: `Ended: ${new Timestamp(Number(endTimeStamp2)).getRelativeTime()} (${endTimeStamp})`}
)
embedMessage.edit({embeds: [embed]})
}
else if (stmt.length > 1 && stmt[winnerIndex].user_id === ctx.userId) {
winnerIndex = Math.floor(Math.random() * stmt.length)
}
else if ((stmt.length === 1 && stmt[winnerIndex].user_id === ctx.userId) || stmt.length === 0) {
embed.setDescription('\u200b')
embed.setFields(
{name: '\u200b', value: `Not enough eligible users`},
{name: '\u200b', value: `Hosted by: <@${ctx.userId}>`},
{name: '\u200b', value: `Ended: ${new Timestamp(Number(endTimeStamp2)).getRelativeTime()} (${endTimeStamp})`}
)
embedMessage.edit({embeds: [embed]})
}
}, intervalTime)
})
db.prepare(`DELETE FROM entrees`).run()
}
})

View File

@@ -25,8 +25,7 @@ export default commandModule({
.setPlaceholder("Name of the emoji")
.setRequired()
.setStyle(TextInputStyle.Short)
.setValue(suggestedName),
);
.setValue(suggestedName));
modal.setComponents(row);
await ctx.showModal(modal);

View File

@@ -1,6 +1,6 @@
import { ownerIDs } from "#constants";
import { commandModule, CommandType } from "@sern/handler";
import { ActionRowBuilder, ButtonBuilder, EmbedBuilder } from "discord.js";
import { ActionRowBuilder, ButtonBuilder, ButtonComponentData, EmbedBuilder } from "discord.js";
export default commandModule({
type: CommandType.Button,
@@ -17,9 +17,7 @@ export default commandModule({
const components = [
new ActionRowBuilder<ButtonBuilder>().setComponents(
ctx.message!.components[0].components.map((c) =>
new ButtonBuilder(c.data).setDisabled(),
),
),
new ButtonBuilder(c.data as ButtonComponentData).setDisabled())),
];
const embed = new EmbedBuilder(ctx.message?.embeds[0]?.data) //

View File

@@ -1,5 +1,5 @@
import { commandModule, CommandType } from "@sern/handler";
import { ActionRowBuilder, ButtonBuilder, EmbedBuilder } from "discord.js";
import { ActionRowBuilder, ButtonBuilder, ButtonComponentData, EmbedBuilder } from "discord.js";
export default commandModule({
type: CommandType.Modal,
@@ -33,9 +33,7 @@ export default commandModule({
const components = [
new ActionRowBuilder<ButtonBuilder>().setComponents(
ctx.message!.components[0].components.map((c) =>
new ButtonBuilder(c.data).setDisabled(),
),
),
new ButtonBuilder(c.data as Readonly<ButtonComponentData>).setDisabled())),
];
const embed = new EmbedBuilder(ctx.message?.embeds[0]?.data) //

View File

@@ -17,15 +17,11 @@ export default commandModule({
name: tagName,
content: tagContent,
keywords: keywords
? [
...new Set(
keywords
? [ ...new Set(keywords
.trim()
.split(",")
.map((c) => c.trim())
.filter((c) => !!c.length),
),
]
.filter((c) => !!c.length))]
: [],
};
const filePath = `./tags.json`;

View File

@@ -18,11 +18,10 @@ export default commandModule({
name: tagName,
content: tagContent,
keywords: keywords
? keywords
.trim()
.split(",")
.map((c) => c.trim())
.filter((c) => !!c.length)
? keywords.trim()
.split(",")
.map((c) => c.trim())
.filter((c) => !!c.length)
: [],
};
const filePath = `./tags.json`;

View File

@@ -1,34 +0,0 @@
import { slashCommand } from "#utils";
import { readFile } from "fs/promises";
import path from "path";
import type { Context, ReplyOptions } from "@sern/handler";
import { ownerOnly, publish } from "#plugins";
import * as fs from "fs";
import { AttachmentBuilder } from "discord.js";
async function ephemeral(ctx: Context, options: ReplyOptions) {
const resolvedOptions = typeof options == "string" ? { content: options } : options;
await ctx.interaction.editReply({ ...resolvedOptions });
}
export default slashCommand({
description: "Fetch logs",
plugins: [publish(), ownerOnly()],
execute: async (ctx, args) => {
try {
const controller = new AbortController();
const logPath = path.join(process.cwd(), "error.log");
const readHandle = readFile(logPath, { signal: controller.signal });
await ctx.interaction.deferReply({ ephemeral: true });
if (!fs.existsSync(logPath)) {
controller.abort();
}
const log = await readHandle;
await ephemeral(ctx, {
files: [new AttachmentBuilder(log).setName("error.log")],
});
} catch (e) {
await ephemeral(ctx, "Couldn't find log. In dev mode?");
}
},
});

View File

@@ -19,7 +19,7 @@ export default slashCommand({
name: "channel",
type: ApplicationCommandOptionType.Channel,
description: "The channel to send the message to",
channelTypes: [ChannelType.GuildText],
channel_types: [ChannelType.GuildText],
required: true,
},
{
@@ -35,7 +35,8 @@ export default slashCommand({
required: true,
},
],
async execute(ctx, [, options]) {
async execute(ctx) {
const options = ctx.options;
const channel = options.getChannel("channel", true) as TextChannel;
const role = new Resolver(options.getString("role", true), ctx.interaction).roles;
const message = options.getString("message", true);
@@ -71,14 +72,11 @@ function createMenu(channel: TextChannel, role: Collection<string, Role>) {
.setMaxValues(role.size)
.setMinValues(0)
.setPlaceholder("Pick some roles here!")
.setOptions(
role.map((r) => {
.setOptions(role.map((r) => {
return {
label: r.name,
value: r.id,
};
}),
);
const row = new ActionRowBuilder<StringSelectMenuBuilder>().setComponents(menu);
return row;
}));
return new ActionRowBuilder<StringSelectMenuBuilder>().setComponents(menu);
}

View File

@@ -14,7 +14,6 @@ export default slashCommand({
required: true,
autocomplete: true,
command: {
onEvent: [],
async execute(ctx) {
const plugins = require(PluginList) as Plugin[];
@@ -45,7 +44,8 @@ export default slashCommand({
}),
),
],
async execute(ctx, [, options]) {
async execute(ctx) {
const { options } = ctx;
const plugins = require(PluginList) as Plugin[];
if (!plugins.length) return ctx.reply("Plugins are uncached, contact Evo!");

View File

@@ -6,10 +6,8 @@ import type { Plugin } from "typings";
export default slashCommand({
plugins: [
publish({
dmPermission: false,
defaultMemberPermissions: ["Administrator"],
}),
publish({ dmPermission: false,
defaultMemberPermissions: ["Administrator"], }),
ownerOnly([Evo]),
],
description: "refresh plugins cache",
@@ -31,7 +29,7 @@ export async function cp(): Promise<number | null> {
const link = `https://raw.githubusercontent.com/sern-handler/awesome-plugins/main/pluginlist.json`;
const resp = await fetch(link).catch(() => null);
if (!resp) return null;
const dataArray = (await resp.json()) as Plugin[];
const dataArray = await resp.json() as Plugin[];
writeFileSync(PluginList, JSON.stringify(dataArray, null, 2), { flag: "w" });
return dataArray.length;

View File

@@ -19,7 +19,6 @@ export default slashCommand({
content: "Something bad happened, Please contact Evo!",
ephemeral: true,
});
const memberCount = `\`${ctx.channel.memberCount}\` member(s) participated in this post!`;
const msgCount = `\`${(ctx.channel.messageCount ?? 0) + 1}\` message(s) are present here`;
const msgSent = `\`${

View File

@@ -41,8 +41,7 @@ export default slashCommand({
? t.toLowerCase().includes(focus.toLowerCase())
: true,
)
.map((t) => ({ name: t, value: t })),
);
.map((t) => ({ name: t, value: t })));
}
},
},
@@ -56,8 +55,8 @@ export default slashCommand({
],
},
],
execute(ctx, args) {
const [, options] = args;
execute(ctx) {
const options = ctx.options;
const subCmd = options.getSubcommand();
switch (subCmd) {
case "list": {
@@ -74,15 +73,15 @@ export default slashCommand({
.setTimestamp();
return embed;
});
const paginator = new Paginator({ embeds }).setSelectMenuOptions(
...Array(embeds.length)
.fill(null)
.map((_, i) => ({
label: embeds[i].data.title!,
value: `${i}`,
default: i === 0,
})),
);
const paginator = new Paginator({ embeds })
.setSelectMenuOptions(
...Array(embeds.length)
.fill(null)
.map((_, i) => ({
label: embeds[i].data.title!,
value: `${i}`,
default: i === 0,
})));
return paginator.run(ctx.interaction);
}

View File

@@ -96,7 +96,7 @@ export default slashCommand({
},
],
execute: async (context, args) => {
const [, options] = args;
const { options } = context;
const subcmd = options.getSubcommand();
const file: TagData[] = require(TagList);

View File

@@ -20,10 +20,8 @@ export default slashCommand({
required: true,
autocomplete: true,
command: {
onEvent: [],
execute: async (autocomplete) => {
const input = autocomplete.options.getFocused();
return autocomplete.respond(fuzz(input)).catch(() => null);
},
},
@@ -49,7 +47,8 @@ export default slashCommand({
type: ApplicationCommandOptionType.Subcommand,
},
],
execute: async (ctx, [, options]) => {
execute: async (ctx) => {
const { options } = ctx;
switch (options.getSubcommand()) {
case "create": {
const reqData = {
@@ -84,7 +83,7 @@ export default slashCommand({
`https://api.srizan.dev/sern/getTime?userid=${option.id}`,
).catch(() => null);
const data = (await request?.json()) as APIResponse;
const data = await request?.json() as APIResponse;
if (!data)
return ctx.reply({

View File

@@ -16,3 +16,8 @@ export const enum Emojis {
export const PluginList = `${process.cwd()}/pluginlist.json`;
export const TagList = `${process.cwd()}/tags.json`;
export const commands = './dist/src/commands';
export const events = './dist/src/events'
export const defaultPrefix= "sern";

15
src/dependencies.d.ts vendored
View File

@@ -5,22 +5,17 @@
*/
import type {
SernEmitter,
Logging,
CoreModuleStore,
ModuleManager,
ErrorHandling,
CoreDependencies,
Singleton,
} from "@sern/handler";
import type { Client } from "discord.js";
import type { SernLogger } from "./utils/Logger";
import type { Octokit } from "@octokit/rest";
declare global {
interface Dependencies extends Dependencies {
"@sern/client": Singleton<Client>;
"@sern/logger": Singleton<SernLogger>;
octokit: Singleton<Octokit>;
interface Dependencies extends CoreDependencies {
"@sern/client": Client;
"@sern/logger": SernLogger;
octokit: Octokit;
process: NodeJS.Process
}
}

15
src/events/embedReact.ts Normal file
View File

@@ -0,0 +1,15 @@
import { discordEvent } from "@sern/handler";
import { db } from "../utils/db.js"
export default discordEvent({
name: 'messageReactionAdd',
execute: async (reaction, potentialWinners) => {
const startTime = reaction.message.createdTimestamp
const stmt = db.prepare(`INSERT INTO entrees(timestamp, user_id) VALUES (?, ?)`)
if (potentialWinners.bot === false) {
stmt.run([startTime, potentialWinners.id])
}
}
})

View File

@@ -1,13 +1,25 @@
import { eventModule, EventType } from "@sern/handler";
import { AnyThreadChannel, EmbedBuilder } from "discord.js";
import { onCorrectThread } from "../plugins/onCorrectThread.js";
import { AnyThreadChannel, ChannelType, EmbedBuilder } from "discord.js";
import { forumID } from "#constants";
function onCorrectThread(thread: AnyThreadChannel, parentId: string, newlyMade: boolean) {
const isBadThread =
!thread.parent ||
thread.parentId !== parentId ||
thread.parent.type !== ChannelType.GuildForum ||
!newlyMade;
if (!isBadThread) {
return true
}
return false;
}
export default eventModule({
type: EventType.Discord,
plugins: [onCorrectThread(forumID)],
name: "threadCreate",
async execute(thread: AnyThreadChannel, _: boolean) {
async execute(thread: AnyThreadChannel, newlyMade: boolean) {
if(!onCorrectThread(thread, forumID, newlyMade)) {
return;
}
if (thread.appliedTags.length > 3)
await thread.setAppliedTags(thread.appliedTags.slice(0, 3));
@@ -17,20 +29,15 @@ export default eventModule({
const list = `• What is your [\`discord.js\`](https://discord.js.org/) version?\n• What is your [\`@sern/handler\`](https://sern.dev) version?\n• If any error is occuring, what error?`;
const embed = new EmbedBuilder()
.setDescription(
`Hello ${msg.author}! Thank you for creating a dedicated post for your issue\n` +
`Please make sure you've read the **__Post Guidelines__**!\n` +
`In the meanwhile you wait for your answer, It is recommended to provide details as much as possible\n`
.concat(list)
.concat(`\n\n\nIssue Solved? Run </solved:1026499792194510939>`),
)
.setDescription(`Hello ${msg.author}! Thank you for creating a dedicated post for your issue\n` +
'Please make sure you\'ve read the **__Post Guidelines__**!\n \
In the meanwhile wait for the answer, It is recommended to provide details as much as possible\n'
.concat(list)
.concat(`\n\n\nIssue Solved? Run </solved:1026499792194510939>`))
.setColor("Random")
.setTimestamp()
.setThumbnail(msg.client.user!.displayAvatarURL({ size: 2048 }))
.setTitle("Things you should know");
await msg.reply({
embeds: [embed],
});
await msg.reply({ embeds: [embed] });
},
});

View File

@@ -1,18 +1,14 @@
import {
controller,
EventControlPlugin,
eventModule,
EventType,
Payload,
PayloadType,
Service,
} from "@sern/handler";
export default eventModule({
name: "module.activate",
type: EventType.Sern,
plugins: [filterFailedActivation()],
execute(payload: Payload & { type: PayloadType.Failure }) {
execute(payload: Payload & { type: 'failure' }) {
const logger = Service("@sern/logger");
logger.warning({
message: `A module (${payload.module?.name} failed to execute: ${payload.reason}`,
@@ -20,12 +16,3 @@ export default eventModule({
},
});
function filterFailedActivation() {
return EventControlPlugin<EventType.Sern>((payload) => {
if (payload.type == PayloadType.Failure) {
return controller.next();
} else {
return controller.stop();
}
});
}

View File

@@ -1,6 +1,7 @@
import "dotenv/config";
import * as config from './config.js'
import { Client, GatewayIntentBits, Partials } from "discord.js";
import { Sern, single, makeDependencies, Service } from "@sern/handler";
import { Sern, makeDependencies, Service } from "@sern/handler";
import { SernLogger } from "#utils";
import { Octokit } from "@octokit/rest";
import { cp } from "./commands/refresh.js";
@@ -11,6 +12,7 @@ const client = new Client({
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMessageReactions,
],
partials: [Partials.GuildMember, Partials.Message, Partials.ThreadMember, Partials.Channel],
sweepers: {
@@ -21,15 +23,11 @@ const client = new Client({
},
});
await makeDependencies({
build: (root) =>
root
.add({ "@sern/client": () => client })
.upsert({ "@sern/logger": () => new SernLogger("info") })
.add({
process: () => process,
octokit: () => new Octokit({ auth: process.env.GITHUB_TOKEN }),
}),
await makeDependencies(root => {
root.add("@sern/client", client);
root.swap("@sern/logger", new SernLogger("info"))
root.add('octokit', new Octokit({ auth: process.env.GITHUB_TOKEN }))
root.add('process', process)
});
Sern.init({

View File

@@ -1,147 +1,180 @@
// @ts-nocheck
/**
* Allows you to set cooldowns (or "ratelimits") for commands
* limits user/channel/guild actions,
* @plugin
* Allows you to set cooldowns (or "ratelimits") for commands, limits user/channel/guild actions.
* An extra function cooldown2 is exported if you want your cooldown to be local to the command.
* @author @trueharuu [<@504698587221852172>]
* @version 1.0.0
* @example
* ```ts
* import { cooldown } from "../plugins/cooldown";
* import { cooldown, cooldown2 } from "../plugins/cooldown";
* import { commandModule } from "@sern/handler";
* //IF you want this cooldown to be local to this command:
* const localCooldown = cooldown2()
* export default commandModule({
* plugins: [cooldown.add( [ ['channel', '1/4'] ] )], // limit to 1 action every 4 seconds per channel
* execute: (ctx) => {
* //your code here
* }
* plugins: [cooldown.add([['channel', '1/4']]), // limit to 1 action every 4 seconds per channel
* localCooldown.add([["user", "1/10"]])], // limit to 1 action every 10 seconds, local to command
* execute: (ctx) => { //your code here }
* })
* ```
* @end
*/
import { CommandControlPlugin, CommandType, Context, controller } from "@sern/handler";
import {
CommandControlPlugin,
CommandType,
Context,
controller,
} from "@sern/handler";
import { GuildMember } from "discord.js";
/**
* actions/seconds
*/
export type CooldownString = `${number}/${number}`;
export interface Cooldown {
location: CooldownLocation;
seconds: number;
actions: number;
location: CooldownLocation;
seconds: number;
actions: number;
}
export enum CooldownLocation {
channel = "channel",
user = "user",
guild = "guild",
channel = "channel",
user = "user",
guild = "guild",
}
export class ExpiryMap<K, V> extends Map<K, V> {
public readonly expiry: number;
constructor(expiry: number = Infinity, iterable: [K, V][] | ReadonlyMap<K, V> = []) {
super(iterable);
this.expiry = expiry;
}
public readonly expiry: number;
constructor(
expiry: number = Infinity,
iterable: [K, V][] | ReadonlyMap<K, V> = [],
) {
super(iterable);
this.expiry = expiry;
}
public set(key: K, value: V, expiry: number = this.expiry): this {
super.set(key, value);
if (expiry !== Infinity)
setTimeout(() => {
super.delete(key);
}, expiry);
return this;
}
public set(key: K, value: V, expiry: number = this.expiry): this {
super.set(key, value);
if (expiry !== Infinity)
setTimeout(() => {
super.delete(key);
}, expiry);
return this;
}
}
export const map = new ExpiryMap<string, number>();
function parseCooldown(location: CooldownLocation, cooldown: CooldownString): Cooldown {
const [actions, seconds] = cooldown.split("/").map((s) => Number(s));
function parseCooldown(
location: CooldownLocation,
cooldown: CooldownString,
): Cooldown {
const [actions, seconds] = cooldown.split("/").map((s) => Number(s));
if (
!Number.isSafeInteger(actions) ||
!Number.isSafeInteger(seconds) ||
actions === undefined ||
seconds === undefined
) {
throw new Error(`Invalid cooldown string: ${cooldown}`);
}
if (
!Number.isSafeInteger(actions) ||
!Number.isSafeInteger(seconds) ||
actions === undefined ||
seconds === undefined
) {
throw new Error(`Invalid cooldown string: ${cooldown}`);
}
return {
actions,
seconds,
location,
};
return {
actions,
seconds,
location,
};
}
function getPropertyForLocation(context: Context, location: CooldownLocation) {
switch (location) {
case CooldownLocation.channel:
return context.channel!.id;
case CooldownLocation.user:
if (!context.member || !(context.member instanceof GuildMember)) {
throw new Error("context.member is not a GuildMember");
}
return context.member.id;
case CooldownLocation.guild:
return context.guildId;
}
switch (location) {
case CooldownLocation.channel:
return context.channel!.id;
case CooldownLocation.user:
if (!context.member || !(context.member instanceof GuildMember)) {
throw new Error("context.member is not a GuildMember");
}
return context.member.id;
case CooldownLocation.guild:
return context.guildId;
}
}
export interface RecievedCooldown {
location: CooldownLocation;
actions: number;
maxActions: number;
seconds: number;
context: Context;
location: CooldownLocation;
actions: number;
maxActions: number;
seconds: number;
context: Context;
}
type CooldownResponse = (cooldown: RecievedCooldown) => any;
function add(
items: Array<[CooldownLocation | keyof typeof CooldownLocation, CooldownString] | Cooldown>,
message?: CooldownResponse,
) {
const raw = items.map((c) => {
if (!Array.isArray(c)) return c;
return parseCooldown(c[0] as CooldownLocation, c[1]);
}) as Array<Cooldown>;
return CommandControlPlugin<CommandType.Both>(async (context, args) => {
for (const { location, actions, seconds } of raw) {
const id = getPropertyForLocation(context, location);
const cooldown = map.get(id!);
function __add(map : ExpiryMap<string, number>,
items: Array<| [CooldownLocation
| keyof typeof CooldownLocation, CooldownString]
| Cooldown>,
message?: CooldownResponse) {
const raw = items.map((c) => {
if (!Array.isArray(c)) return c;
return parseCooldown(c[0] as CooldownLocation, c[1]);
}) as Array<Cooldown>;
if (!cooldown) {
map.set(id!, 1, seconds * 1000);
continue;
}
return CommandControlPlugin<CommandType.Both>(async (context, args) => {
for (const { location, actions, seconds } of raw) {
const id = getPropertyForLocation(context, location);
const cooldown = map.get(id!);
if (cooldown >= actions) {
if (message) {
await message({
location,
actions: cooldown,
maxActions: actions,
seconds,
context,
});
}
return controller.stop();
}
if (!cooldown) {
map.set(id!, 1, seconds * 1000);
continue;
}
map.set(id!, cooldown + 1, seconds * 1000);
}
return controller.next();
});
if (cooldown >= actions) {
if (message) {
await message({
location,
actions: cooldown,
maxActions: actions,
seconds,
context,
});
}
return controller.stop();
}
map.set(id!, cooldown + 1, seconds * 1000);
}
return controller.next();
});
}
type Location = (value: CooldownString) => ReturnType<typeof add>;
type Location = (value: CooldownString) => ReturnType<typeof __add>;
const locations: Record<CooldownLocation, Location> = {
[CooldownLocation.channel]: (value) => add([[CooldownLocation.channel, value]]),
[CooldownLocation.user]: (value) => add([[CooldownLocation.user, value]]),
[CooldownLocation.guild]: (value) => add([[CooldownLocation.guild, value]]),
const locationsFn = (map: ExpiryMap<string, number>)=> ({
[CooldownLocation.channel]: (value) => __add(map, [[CooldownLocation.channel, value]]),
[CooldownLocation.user]: (value) => __add(map, [[CooldownLocation.user, value]]),
[CooldownLocation.guild]: (value) => __add(map, [[CooldownLocation.guild, value]]),
} as Record<CooldownLocation, Location>);
export const cooldown2 = () => {
const cooldownMap = new ExpiryMap<string, number>();
return {
add:(items: Array<| [CooldownLocation
| keyof typeof CooldownLocation, CooldownString]
| Cooldown>,
message?: CooldownResponse) => __add(cooldownMap, items, message),
locations: locationsFn(cooldownMap),
map: cooldownMap
}
};
const map = new ExpiryMap<string, number>();
export const cooldown = {
add,
locations,
map,
};
locations: locationsFn(map),
add: (items: Array<| [CooldownLocation
| keyof typeof CooldownLocation, CooldownString]
| Cooldown>,
message?: CooldownResponse) => __add(map, items, message)
}

View File

@@ -1,16 +0,0 @@
import { controller, DiscordEventControlPlugin } from "@sern/handler";
import { ChannelType } from "discord.js";
export function onCorrectThread(parentId: string) {
return DiscordEventControlPlugin("threadCreate", (thread, newlyMade) => {
const isBadThread =
!thread.parent ||
thread.parentId !== parentId ||
thread.parent.type !== ChannelType.GuildForum ||
!newlyMade;
if (!isBadThread) {
return controller.next();
}
return controller.stop();
});
}

View File

@@ -25,7 +25,6 @@ import {
controller,
SernOptionsData,
SlashCommand,
Service,
} from "@sern/handler";
import {
ApplicationCommandData,

View File

@@ -1,73 +0,0 @@
// import type { SernLogger } from "./Logger";
// import { readdir } from "fs/promises";
// import path from "path";
// import type { Client } from "discord.js";
// import { ApplicationCommandType, basename } from "discord.js";
// import type { BothCommand, CommandModule, ContextMenuMsg, ContextMenuUser, SlashCommand } from "@sern/handler";
// import { CommandType } from "@sern/handler";
//
// async function* getFiles(dir: string): AsyncGenerator<string> {
// const dirents = await readdir(dir, { withFileTypes: true });
// for (const dirent of dirents) {
// const res = path.resolve(dir, dirent.name);
// if (dirent.isDirectory()) {
// yield* getFiles(res);
// } else {
// yield res;
// }
// }
// }
//
// export class CommandSyncer {
// private commandsPath = "dist/src/commands";
//
// constructor(
// private logger: SernLogger,
// private client: Client,
// private scopedGuilds : string[] = []
// ) {
// setTimeout(() => {
// this
// .sync()
// .catch((e) => logger.error({ message: e ?? "Something went wrong with syncing" }));
// }, 20_000)
// }
// private publishable(module: CommandModule) {
// const publishableTypes = (CommandType.Both | CommandType.CtxUser | CommandType.CtxMsg | CommandType.Slash)
// return ((publishableTypes & ~CommandType.Text) & module.type) != 0
// }
// private async handleSlashCommand(module: SlashCommand | BothCommand, resolvedName: string) {
// this.logger.debug({ message: `Checking if ${resolvedName} is already registered` });
// if (this.scopedGuilds.length) {
// for (const guildId of this.scopedGuilds) {
// const guild = await this.client.guilds.fetch(guildId).catch(() => null);
//
// if (!guild) throw new Error(`Found no Guild with id ${guildId}!`);
// console.log(guild.name)
// console.log(await guild.commands.fetch())
// }
// }
// }
// private handleContextMenus(module: ContextMenuUser | ContextMenuMsg, resolvedName: string) {
//
// }
// private async sync() {
// this.logger.info({ message: "Syncing commands" });
// for await(const path of getFiles(this.commandsPath)) {
// const module = await import("file:///"+path).then(imp => imp.default) as CommandModule; //i would retrieve from the module store, but its a little bugged since
// if(this.publishable(module)) {
// const resolvedName = module.name ?? basename(path).slice(0, -3)
// switch(module.type) {
// case CommandType.Both:
// case CommandType.Slash: {
// await this.handleSlashCommand(module, resolvedName)
// } break;
// case CommandType.CtxMsg:
// case CommandType.CtxUser: {
// this.handleContextMenus(module, resolvedName)
// } break;
// }
// }
// }
// }
// }

View File

@@ -1,22 +1,25 @@
import {
AnyCommandPlugin,
BaseOptions,
commandModule,
CommandType,
Context,
SernSubCommandData,
SernSubCommandGroupData,
SlashOptions,
Plugin,
SDT,
SlashCommand
} from "@sern/handler";
import { ChatInputCommandInteraction } from "discord.js";
export function slashCommand(data: {
name?: string;
description: string;
plugins?: AnyCommandPlugin[];
options?: (SernSubCommandData | SernSubCommandGroupData | BaseOptions)[] | undefined;
execute: (ctx: Context, args: ["slash", SlashOptions]) => any;
plugins?: Plugin[];
options?: SlashCommand['options'];
execute: (ctx: Context & { get options(): ChatInputCommandInteraction['options'] }, args: SDT) => any;
}) {
//Weird fix for explicit undefined fields in an object
const resolvedData = { type: CommandType.Slash, ...data } as const;
return commandModule(resolvedData);
}

6
src/utils/db.ts Normal file
View File

@@ -0,0 +1,6 @@
import Database from 'better-sqlite3';
export const db = new Database('giveaway.db');
db.pragma('journal_mode = WAL');
db.exec(`CREATE TABLE IF NOT EXISTS entrees(timestamp, user_id)`);

375
yarn.lock
View File

@@ -479,15 +479,24 @@ __metadata:
languageName: node
linkType: hard
"@sern/handler@npm:^3.3.2":
version: 3.3.2
resolution: "@sern/handler@npm:3.3.2"
"@sern/handler@npm:^4.0.0":
version: 4.0.1
resolution: "@sern/handler@npm:4.0.1"
dependencies:
"@sern/ioc": ^1.1.0
callsites: ^3.1.0
iti: ^0.6.0
cron: ^3.1.7
deepmerge: ^4.3.1
rxjs: ^7.8.0
ts-results-es: ^4.0.0
checksum: a405f9f4e914245dc0c0a9091508871a2cdbb50947a71797ec4faa64ba0ec775dc8b9aa0f116c4699752520ca4b5f54b624898d7da0f53375e4534a698d0c3d4
ts-results-es: ^4.1.0
checksum: ab461fa953f2086b5b8ebc089078a55535d0cccd6f5b3210c45bde33022d3be1f3b47bb3ab251b129c7ed33bfe08b873416c70aace50c101ac03d0097206552a
languageName: node
linkType: hard
"@sern/ioc@npm:^1.1.0":
version: 1.1.0
resolution: "@sern/ioc@npm:1.1.0"
checksum: 0882ef51c3fcd28e7fe803762f2a8d7eb3dca4e494d3475ef7d4a43158d3b24243bda3679c3d58485c89bdc820719d22351007503e44b5cf8e6f2d0efe342921
languageName: node
linkType: hard
@@ -498,6 +507,13 @@ __metadata:
languageName: node
linkType: hard
"@types/luxon@npm:~3.4.0":
version: 3.4.2
resolution: "@types/luxon@npm:3.4.2"
checksum: 6f92d5bd02e89f310395753506bcd9cef3a56f5940f7a50db2a2b9822bce753553ac767d143cb5b4f9ed5ddd4a84e64f89ff538082ceb4d18739af7781b56925
languageName: node
linkType: hard
"@types/node@npm:*":
version: 18.11.18
resolution: "@types/node@npm:18.11.18"
@@ -643,6 +659,13 @@ __metadata:
languageName: node
linkType: hard
"base64-js@npm:^1.3.1":
version: 1.5.1
resolution: "base64-js@npm:1.5.1"
checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005
languageName: node
linkType: hard
"before-after-hook@npm:^2.2.0":
version: 2.2.3
resolution: "before-after-hook@npm:2.2.3"
@@ -650,6 +673,17 @@ __metadata:
languageName: node
linkType: hard
"better-sqlite3@npm:^11.1.2":
version: 11.1.2
resolution: "better-sqlite3@npm:11.1.2"
dependencies:
bindings: ^1.5.0
node-gyp: latest
prebuild-install: ^7.1.1
checksum: fec9e5ea8236206ef2b334ae1d779217cbcd327f4d1822e745148c810fea3412c54483fff0cef5b3133b9a7fd1311ae7c7498b54b274074c6c04bd8e5c9dc54c
languageName: node
linkType: hard
"binary-extensions@npm:^2.0.0":
version: 2.2.0
resolution: "binary-extensions@npm:2.2.0"
@@ -657,6 +691,26 @@ __metadata:
languageName: node
linkType: hard
"bindings@npm:^1.5.0":
version: 1.5.0
resolution: "bindings@npm:1.5.0"
dependencies:
file-uri-to-path: 1.0.0
checksum: 65b6b48095717c2e6105a021a7da4ea435aa8d3d3cd085cb9e85bcb6e5773cf318c4745c3f7c504412855940b585bdf9b918236612a1c7a7942491de176f1ae7
languageName: node
linkType: hard
"bl@npm:^4.0.3":
version: 4.1.0
resolution: "bl@npm:4.1.0"
dependencies:
buffer: ^5.5.0
inherits: ^2.0.4
readable-stream: ^3.4.0
checksum: 9e8521fa7e83aa9427c6f8ccdcba6e8167ef30cc9a22df26effcc5ab682ef91d2cbc23a239f945d099289e4bbcfae7a192e9c28c84c6202e710a0dfec3722662
languageName: node
linkType: hard
"bmp-js@npm:^0.1.0":
version: 0.1.0
resolution: "bmp-js@npm:0.1.0"
@@ -692,6 +746,16 @@ __metadata:
languageName: node
linkType: hard
"buffer@npm:^5.5.0":
version: 5.7.1
resolution: "buffer@npm:5.7.1"
dependencies:
base64-js: ^1.3.1
ieee754: ^1.1.13
checksum: e2cf8429e1c4c7b8cbd30834ac09bd61da46ce35f5c22a78e6c2f04497d6d25541b16881e30a019c6fd3154150650ccee27a308eff3e26229d788bbdeb08ab84
languageName: node
linkType: hard
"bundle-require@npm:^4.0.0":
version: 4.0.1
resolution: "bundle-require@npm:4.0.1"
@@ -771,6 +835,13 @@ __metadata:
languageName: node
linkType: hard
"chownr@npm:^1.1.1":
version: 1.1.4
resolution: "chownr@npm:1.1.4"
checksum: 115648f8eb38bac5e41c3857f3e663f9c39ed6480d1349977c4d96c95a47266fcacc5a5aabf3cb6c481e22d72f41992827db47301851766c4fd77ac21a4f081d
languageName: node
linkType: hard
"chownr@npm:^2.0.0":
version: 2.0.0
resolution: "chownr@npm:2.0.0"
@@ -868,6 +939,16 @@ __metadata:
languageName: node
linkType: hard
"cron@npm:^3.1.7":
version: 3.1.7
resolution: "cron@npm:3.1.7"
dependencies:
"@types/luxon": ~3.4.0
luxon: ~3.4.0
checksum: d98ee5297543c138221d96dd49270bf6576db80134e6041f4ce4a3c0cb6060863d76910209b34fee66fbf134461449ec3bd283d6a76d1c50da220cde7fc10c65
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.3":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
@@ -879,6 +960,13 @@ __metadata:
languageName: node
linkType: hard
"date-fns@npm:^3.6.0":
version: 3.6.0
resolution: "date-fns@npm:3.6.0"
checksum: 0daa1e9a436cf99f9f2ae9232b55e11f3dd46132bee10987164f3eebd29f245b2e066d7d7db40782627411ecf18551d8f4c9fcdf2226e48bb66545407d448ab7
languageName: node
linkType: hard
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.3":
version: 4.3.4
resolution: "debug@npm:4.3.4"
@@ -891,6 +979,29 @@ __metadata:
languageName: node
linkType: hard
"decompress-response@npm:^6.0.0":
version: 6.0.0
resolution: "decompress-response@npm:6.0.0"
dependencies:
mimic-response: ^3.1.0
checksum: d377cf47e02d805e283866c3f50d3d21578b779731e8c5072d6ce8c13cc31493db1c2f6784da9d1d5250822120cefa44f1deab112d5981015f2e17444b763812
languageName: node
linkType: hard
"deep-extend@npm:^0.6.0":
version: 0.6.0
resolution: "deep-extend@npm:0.6.0"
checksum: 7be7e5a8d468d6b10e6a67c3de828f55001b6eb515d014f7aeb9066ce36bd5717161eb47d6a0f7bed8a9083935b465bc163ee2581c8b128d29bf61092fdf57a7
languageName: node
linkType: hard
"deepmerge@npm:^4.3.1":
version: 4.3.1
resolution: "deepmerge@npm:4.3.1"
checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052
languageName: node
linkType: hard
"delegates@npm:^1.0.0":
version: 1.0.0
resolution: "delegates@npm:1.0.0"
@@ -912,6 +1023,13 @@ __metadata:
languageName: node
linkType: hard
"detect-libc@npm:^2.0.0":
version: 2.0.3
resolution: "detect-libc@npm:2.0.3"
checksum: 2ba6a939ae55f189aea996ac67afceb650413c7a34726ee92c40fb0deb2400d57ef94631a8a3f052055eea7efb0f99a9b5e6ce923415daa3e68221f963cfc27d
languageName: node
linkType: hard
"dir-glob@npm:^3.0.1":
version: 3.0.1
resolution: "dir-glob@npm:3.0.1"
@@ -980,6 +1098,15 @@ __metadata:
languageName: node
linkType: hard
"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1":
version: 1.4.4
resolution: "end-of-stream@npm:1.4.4"
dependencies:
once: ^1.4.0
checksum: 530a5a5a1e517e962854a31693dbb5c0b2fc40b46dad2a56a2deec656ca040631124f4795823acc68238147805f8b021abbe221f4afed5ef3c8e8efc2024908b
languageName: node
linkType: hard
"env-paths@npm:^2.2.0":
version: 2.2.1
resolution: "env-paths@npm:2.2.1"
@@ -1088,6 +1215,13 @@ __metadata:
languageName: node
linkType: hard
"expand-template@npm:^2.0.3":
version: 2.0.3
resolution: "expand-template@npm:2.0.3"
checksum: 588c19847216421ed92befb521767b7018dc88f88b0576df98cb242f20961425e96a92cbece525ef28cc5becceae5d544ae0f5b9b5e2aa05acb13716ca5b3099
languageName: node
linkType: hard
"fast-deep-equal@npm:3.1.3, fast-deep-equal@npm:^3.1.3":
version: 3.1.3
resolution: "fast-deep-equal@npm:3.1.3"
@@ -1124,6 +1258,13 @@ __metadata:
languageName: node
linkType: hard
"file-uri-to-path@npm:1.0.0":
version: 1.0.0
resolution: "file-uri-to-path@npm:1.0.0"
checksum: b648580bdd893a008c92c7ecc96c3ee57a5e7b6c4c18a9a09b44fb5d36d79146f8e442578bc0e173dc027adf3987e254ba1dfd6e3ec998b7c282873010502144
languageName: node
linkType: hard
"fill-range@npm:^7.0.1":
version: 7.0.1
resolution: "fill-range@npm:7.0.1"
@@ -1140,6 +1281,13 @@ __metadata:
languageName: node
linkType: hard
"fs-constants@npm:^1.0.0":
version: 1.0.0
resolution: "fs-constants@npm:1.0.0"
checksum: 18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d
languageName: node
linkType: hard
"fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0":
version: 2.1.0
resolution: "fs-minipass@npm:2.1.0"
@@ -1198,6 +1346,13 @@ __metadata:
languageName: node
linkType: hard
"github-from-package@npm:0.0.0":
version: 0.0.0
resolution: "github-from-package@npm:0.0.0"
checksum: 14e448192a35c1e42efee94c9d01a10f42fe790375891a24b25261246ce9336ab9df5d274585aedd4568f7922246c2a78b8a8cd2571bfe99c693a9718e7dd0e3
languageName: node
linkType: hard
"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2":
version: 5.1.2
resolution: "glob-parent@npm:5.1.2"
@@ -1345,6 +1500,13 @@ __metadata:
languageName: node
linkType: hard
"ieee754@npm:^1.1.13":
version: 1.2.1
resolution: "ieee754@npm:1.2.1"
checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e
languageName: node
linkType: hard
"ignore@npm:^5.2.0":
version: 5.2.0
resolution: "ignore@npm:5.2.0"
@@ -1383,13 +1545,20 @@ __metadata:
languageName: node
linkType: hard
"inherits@npm:2, inherits@npm:^2.0.3":
"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:^2.0.4":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1
languageName: node
linkType: hard
"ini@npm:~1.3.0":
version: 1.3.8
resolution: "ini@npm:1.3.8"
checksum: dfd98b0ca3a4fc1e323e38a6c8eb8936e31a97a918d3b377649ea15bdb15d481207a0dda1021efbd86b464cae29a0d33c1d7dcaf6c5672bee17fa849bc50a1b3
languageName: node
linkType: hard
"ip@npm:^2.0.0":
version: 2.0.0
resolution: "ip@npm:2.0.0"
@@ -1485,15 +1654,6 @@ __metadata:
languageName: node
linkType: hard
"iti@npm:^0.6.0":
version: 0.6.0
resolution: "iti@npm:0.6.0"
dependencies:
utility-types: ^3.10.0
checksum: 19e484aa8b00bf57642c73c56b658d06d70d7b5acf5725a6aca9948c6b3c8d1fab18d71fb25f482a13d8c6acac137799fa80e7dbdc97cc24ed5afc94f03811e3
languageName: node
linkType: hard
"jclass@npm:^1.0.1":
version: 1.2.1
resolution: "jclass@npm:1.2.1"
@@ -1593,6 +1753,13 @@ __metadata:
languageName: node
linkType: hard
"luxon@npm:~3.4.0":
version: 3.4.4
resolution: "luxon@npm:3.4.4"
checksum: 36c1f99c4796ee4bfddf7dc94fa87815add43ebc44c8934c924946260a58512f0fd2743a629302885df7f35ccbd2d13f178c15df046d0e3b6eb71db178f1c60c
languageName: node
linkType: hard
"magic-bytes.js@npm:^1.5.0":
version: 1.8.0
resolution: "magic-bytes.js@npm:1.8.0"
@@ -1655,6 +1822,13 @@ __metadata:
languageName: node
linkType: hard
"mimic-response@npm:^3.1.0":
version: 3.1.0
resolution: "mimic-response@npm:3.1.0"
checksum: 25739fee32c17f433626bf19f016df9036b75b3d84a3046c7d156e72ec963dd29d7fc8a302f55a3d6c5a4ff24259676b15d915aad6480815a969ff2ec0836867
languageName: node
linkType: hard
"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
@@ -1673,6 +1847,13 @@ __metadata:
languageName: node
linkType: hard
"minimist@npm:^1.2.0, minimist@npm:^1.2.3":
version: 1.2.8
resolution: "minimist@npm:1.2.8"
checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0
languageName: node
linkType: hard
"minipass-collect@npm:^1.0.2":
version: 1.0.2
resolution: "minipass-collect@npm:1.0.2"
@@ -1743,6 +1924,13 @@ __metadata:
languageName: node
linkType: hard
"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3":
version: 0.5.3
resolution: "mkdirp-classic@npm:0.5.3"
checksum: 3f4e088208270bbcc148d53b73e9a5bd9eef05ad2cbf3b3d0ff8795278d50dd1d11a8ef1875ff5aea3fa888931f95bfcb2ad5b7c1061cfefd6284d199e6776ac
languageName: node
linkType: hard
"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4":
version: 1.0.4
resolution: "mkdirp@npm:1.0.4"
@@ -1777,6 +1965,13 @@ __metadata:
languageName: node
linkType: hard
"napi-build-utils@npm:^1.0.1":
version: 1.0.2
resolution: "napi-build-utils@npm:1.0.2"
checksum: 06c14271ee966e108d55ae109f340976a9556c8603e888037145d6522726aebe89dd0c861b4b83947feaf6d39e79e08817559e8693deedc2c94e82c5cbd090c7
languageName: node
linkType: hard
"negotiator@npm:^0.6.3":
version: 0.6.3
resolution: "negotiator@npm:0.6.3"
@@ -1784,6 +1979,15 @@ __metadata:
languageName: node
linkType: hard
"node-abi@npm:^3.3.0":
version: 3.65.0
resolution: "node-abi@npm:3.65.0"
dependencies:
semver: ^7.3.5
checksum: 5a60f2b0c73fe0a1123e581bd99e43729f4aa3f4b9b19f1915567128d52540e8f812474410a446cd77d708a3a1139e0b2abf1d0823ba6b5f5d47aa4345931706
languageName: node
linkType: hard
"node-fetch@npm:^2.6.7":
version: 2.6.9
resolution: "node-fetch@npm:2.6.9"
@@ -1878,7 +2082,7 @@ __metadata:
languageName: node
linkType: hard
"once@npm:^1.3.0, once@npm:^1.4.0":
"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
dependencies:
@@ -1976,6 +2180,28 @@ __metadata:
languageName: node
linkType: hard
"prebuild-install@npm:^7.1.1":
version: 7.1.2
resolution: "prebuild-install@npm:7.1.2"
dependencies:
detect-libc: ^2.0.0
expand-template: ^2.0.3
github-from-package: 0.0.0
minimist: ^1.2.3
mkdirp-classic: ^0.5.3
napi-build-utils: ^1.0.1
node-abi: ^3.3.0
pump: ^3.0.0
rc: ^1.2.7
simple-get: ^4.0.0
tar-fs: ^2.0.0
tunnel-agent: ^0.6.0
bin:
prebuild-install: bin.js
checksum: 543dadf8c60e004ae9529e6013ca0cbeac8ef38b5f5ba5518cb0b622fe7f8758b34e4b5cb1a791db3cdc9d2281766302df6088bd1a225f206925d6fee17d6c5c
languageName: node
linkType: hard
"promise-inflight@npm:^1.0.1":
version: 1.0.1
resolution: "promise-inflight@npm:1.0.1"
@@ -1993,6 +2219,16 @@ __metadata:
languageName: node
linkType: hard
"pump@npm:^3.0.0":
version: 3.0.0
resolution: "pump@npm:3.0.0"
dependencies:
end-of-stream: ^1.1.0
once: ^1.3.1
checksum: e42e9229fba14732593a718b04cb5e1cfef8254544870997e0ecd9732b189a48e1256e4e5478148ecb47c8511dca2b09eae56b4d0aad8009e6fac8072923cfc9
languageName: node
linkType: hard
"punycode@npm:^2.1.0":
version: 2.1.1
resolution: "punycode@npm:2.1.1"
@@ -2007,6 +2243,31 @@ __metadata:
languageName: node
linkType: hard
"rc@npm:^1.2.7":
version: 1.2.8
resolution: "rc@npm:1.2.8"
dependencies:
deep-extend: ^0.6.0
ini: ~1.3.0
minimist: ^1.2.0
strip-json-comments: ~2.0.1
bin:
rc: ./cli.js
checksum: 2e26e052f8be2abd64e6d1dabfbd7be03f80ec18ccbc49562d31f617d0015fbdbcf0f9eed30346ea6ab789e0fdfe4337f033f8016efdbee0df5354751842080e
languageName: node
linkType: hard
"readable-stream@npm:^3.1.1":
version: 3.6.2
resolution: "readable-stream@npm:3.6.2"
dependencies:
inherits: ^2.0.3
string_decoder: ^1.1.1
util-deprecate: ^1.0.1
checksum: bdcbe6c22e846b6af075e32cf8f4751c2576238c5043169a1c221c92ee2878458a816a4ea33f4c67623c0b6827c8a400409bfb3cf0bf3381392d0b1dfb52ac8d
languageName: node
linkType: hard
"readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0":
version: 3.6.0
resolution: "readable-stream@npm:3.6.0"
@@ -2098,7 +2359,7 @@ __metadata:
languageName: node
linkType: hard
"safe-buffer@npm:~5.2.0":
"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491
@@ -2135,9 +2396,11 @@ __metadata:
resolution: "sern-community@workspace:."
dependencies:
"@octokit/rest": 19.0.7
"@sern/handler": ^3.3.2
"@sern/handler": ^4.0.0
"@types/node": 18.16.3
"@types/string-similarity": 4.0.0
better-sqlite3: ^11.1.2
date-fns: ^3.6.0
discord.js: ^14.14.1
dotenv: 16.0.3
jsdoc-parse-plus: 1.3.0
@@ -2181,6 +2444,24 @@ __metadata:
languageName: node
linkType: hard
"simple-concat@npm:^1.0.0":
version: 1.0.1
resolution: "simple-concat@npm:1.0.1"
checksum: 4d211042cc3d73a718c21ac6c4e7d7a0363e184be6a5ad25c8a1502e49df6d0a0253979e3d50dbdd3f60ef6c6c58d756b5d66ac1e05cda9cacd2e9fc59e3876a
languageName: node
linkType: hard
"simple-get@npm:^4.0.0":
version: 4.0.1
resolution: "simple-get@npm:4.0.1"
dependencies:
decompress-response: ^6.0.0
once: ^1.3.1
simple-concat: ^1.0.0
checksum: e4132fd27cf7af230d853fa45c1b8ce900cb430dd0a3c6d3829649fe4f2b26574c803698076c4006450efb0fad2ba8c5455fbb5755d4b0a5ec42d4f12b31d27e
languageName: node
linkType: hard
"simple-swizzle@npm:^0.2.2":
version: 0.2.2
resolution: "simple-swizzle@npm:0.2.2"
@@ -2300,6 +2581,13 @@ __metadata:
languageName: node
linkType: hard
"strip-json-comments@npm:~2.0.1":
version: 2.0.1
resolution: "strip-json-comments@npm:2.0.1"
checksum: 1074ccb63270d32ca28edfb0a281c96b94dc679077828135141f27d52a5a398ef5e78bcf22809d23cadc2b81dfbe345eb5fd8699b385c8b1128907dec4a7d1e1
languageName: node
linkType: hard
"sucrase@npm:^3.20.3":
version: 3.25.0
resolution: "sucrase@npm:3.25.0"
@@ -2317,6 +2605,31 @@ __metadata:
languageName: node
linkType: hard
"tar-fs@npm:^2.0.0":
version: 2.1.1
resolution: "tar-fs@npm:2.1.1"
dependencies:
chownr: ^1.1.1
mkdirp-classic: ^0.5.2
pump: ^3.0.0
tar-stream: ^2.1.4
checksum: f5b9a70059f5b2969e65f037b4e4da2daf0fa762d3d232ffd96e819e3f94665dbbbe62f76f084f1acb4dbdcce16c6e4dac08d12ffc6d24b8d76720f4d9cf032d
languageName: node
linkType: hard
"tar-stream@npm:^2.1.4":
version: 2.2.0
resolution: "tar-stream@npm:2.2.0"
dependencies:
bl: ^4.0.3
end-of-stream: ^1.4.1
fs-constants: ^1.0.0
inherits: ^2.0.3
readable-stream: ^3.1.1
checksum: 699831a8b97666ef50021c767f84924cfee21c142c2eb0e79c63254e140e6408d6d55a065a2992548e72b06de39237ef2b802b99e3ece93ca3904a37622a66f3
languageName: node
linkType: hard
"tar@npm:^6.1.11, tar@npm:^6.1.2":
version: 6.1.12
resolution: "tar@npm:6.1.12"
@@ -2445,10 +2758,10 @@ __metadata:
languageName: node
linkType: hard
"ts-results-es@npm:^4.0.0":
version: 4.0.0
resolution: "ts-results-es@npm:4.0.0"
checksum: 32a7059491e36d06c5a1084fe9be8021a0beb2d94a94b0c3fa85dc3e96561bf34fb8fd60ebe661064c9fc2bafcf437b6b65f119e8d7497af7f76cda9d9a2a945
"ts-results-es@npm:^4.1.0":
version: 4.2.0
resolution: "ts-results-es@npm:4.2.0"
checksum: ff475c2f6d44377e0204211e6eafdbcabddf3ad09d40540ad5dee3d817eefbd48c07a21f5ad86864ef82cd8a5542a266af9dd8dd4d58d4766fdd6e79370519bb
languageName: node
linkType: hard
@@ -2502,6 +2815,15 @@ __metadata:
languageName: node
linkType: hard
"tunnel-agent@npm:^0.6.0":
version: 0.6.0
resolution: "tunnel-agent@npm:0.6.0"
dependencies:
safe-buffer: ^5.0.1
checksum: 05f6510358f8afc62a057b8b692f05d70c1782b70db86d6a1e0d5e28a32389e52fa6e7707b6c5ecccacc031462e4bc35af85ecfe4bbc341767917b7cf6965711
languageName: node
linkType: hard
"typescript@npm:5.0.4":
version: 5.0.4
resolution: "typescript@npm:5.0.4"
@@ -2572,13 +2894,6 @@ __metadata:
languageName: node
linkType: hard
"utility-types@npm:^3.10.0":
version: 3.10.0
resolution: "utility-types@npm:3.10.0"
checksum: 8f274415c6196ab62883b8bd98c9d2f8829b58016e4269aaa1ebd84184ac5dda7dc2ca45800c0d5e0e0650966ba063bf9a412aaeaea6850ca4440a391283d5c8
languageName: node
linkType: hard
"wasm-feature-detect@npm:^1.2.11":
version: 1.6.1
resolution: "wasm-feature-detect@npm:1.6.1"