This commit is contained in:
Kalle 2023-09-09 14:02:38 +03:00
parent d37fb57a73
commit c8ccdb35b9
23 changed files with 0 additions and 1878 deletions

View File

@ -5,7 +5,6 @@ module.exports = {
tsconfigRootDir: __dirname,
project: ["./tsconfig.json"],
},
ignorePatterns: ["discord-bot/**/*"],
plugins: ["@typescript-eslint"],
extends: [
"eslint:recommended",

View File

@ -112,7 +112,6 @@ sendou.ink/
│ ├── styles/ -- All .css files of the project for styling
│ ├── utils/ -- Random helper functions used in many places
│ └── permissions.ts / -- What actions are allowed. Separated by frontend and backend as frontend has constraints based on what user sees.
├── discord-bot/ -- Lohi Discord bot that works together with sendou.ink
├── migrations/ -- Database migrations
├── public/ -- Images, built assets etc. static files to be served as is
└── scripts/ -- Stand-alone scripts to be run outside of the app

View File

@ -1,5 +0,0 @@
BOT_TOKEN=
TEST_GUILD_ID=
BOT_ID=
SENDOU_INK_URL=http://localhost:5800
LOHI_TOKEN=salmon

View File

@ -1,88 +0,0 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import { CacheType, CommandInteraction, GuildMember } from "discord.js";
import ids from "../ids";
import type { BotCommand } from "../types";
import { isPlusTierRoleId, plusTierToRoleId, usersWithAccess } from "../utils";
const COMMAND_NAME = "access";
export const accessCommand: BotCommand = {
guilds: [ids.guilds.plusServer],
name: COMMAND_NAME,
builder: new SlashCommandBuilder()
.setName(COMMAND_NAME)
.setDescription(
"Get the corresponding Plus Server membership role if you have access",
),
execute: async ({ interaction }) => {
await givePlusRoleToMember({
interaction,
member: interaction.member as GuildMember,
});
},
};
export async function givePlusRoleToMember({
interaction,
member,
}: {
interaction?: CommandInteraction<CacheType>;
member: GuildMember;
}) {
const { users } = await usersWithAccessCached();
const usersPlusTier = users[member.id];
if (!usersPlusTier) {
return interaction?.reply({
content: "You currently don't have access",
ephemeral: true,
});
}
const targetRoleId = plusTierToRoleId(usersPlusTier);
if (!targetRoleId) {
throw new Error(`No role for plus tier ${usersPlusTier}`);
}
const roleManager = member.roles;
const usersRoleIds = roleManager.cache.map((r) => r.id);
const alreadyHasRole = usersRoleIds.some((id) => id === targetRoleId);
if (alreadyHasRole) {
return interaction?.reply({
content: `You have access to +${usersPlusTier} and already have the role for it`,
ephemeral: true,
});
}
const roleIdsToDelete = usersRoleIds.filter(isPlusTierRoleId);
for (const roleIdToDelete of roleIdsToDelete) {
await roleManager.remove(roleIdToDelete);
}
await roleManager.add(targetRoleId);
if (interaction) {
return interaction.reply({
content: `Gave the role giving access to +${usersPlusTier}`,
ephemeral: true,
});
}
}
let cachedAt: Date | null = null;
let usersWithAccessCache: ReturnType<typeof usersWithAccess> | null = null;
async function usersWithAccessCached() {
// fetch fresh data every hour because votings end at the hour
if (
!cachedAt ||
!usersWithAccessCache ||
cachedAt.getHours() !== new Date().getHours()
) {
cachedAt = new Date();
usersWithAccessCache = usersWithAccess();
}
return usersWithAccessCache;
}

View File

@ -1,79 +0,0 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import type { GuildMemberRoleManager } from "discord.js";
import ids from "../ids";
import type { BotCommand } from "../types";
import { patronsFiveDollarsAndOver } from "../utils";
const COMMAND_NAME = "color";
const ACTION_ARG = "hex";
const ADMIN_DISCORD_ID = "79237403620945920";
export const colorCommand: BotCommand = {
guilds: [ids.guilds.plusServer, ids.guilds.sendou],
name: COMMAND_NAME,
builder: new SlashCommandBuilder()
.setName(COMMAND_NAME)
.setDescription(
"Get/update color role (Supporter tier on Patreon or higher needed)",
)
.addStringOption((option) =>
option
.setName(ACTION_ARG)
.setDescription("Hex code of the color role")
.setRequired(true),
),
execute: async ({ interaction, client }) => {
let hexCode = interaction.options.getString(ACTION_ARG);
if (!hexCode.startsWith("#")) {
hexCode = `#${hexCode}`;
}
if (!validateHexCode(hexCode)) {
return interaction.reply({
content:
"Hex code is invalid. Google color picker to find a valid one or DM Sendou for help.",
ephemeral: true,
});
}
const patrons = await patronsFiveDollarsAndOver();
if (
!patrons.some((p) => p.discordId === interaction.user.id) &&
interaction.user.id !== ADMIN_DISCORD_ID
) {
return interaction.reply({
content:
"This command is only available to patrons of 'Supporter' tier or higher https://www.patreon.com/sendou",
ephemeral: true,
});
}
const memberRoles = interaction.member?.roles as GuildMemberRoleManager;
const roleToUpdate = memberRoles.cache.find((role) =>
role.name.includes("!"),
);
if (roleToUpdate) {
await roleToUpdate.edit({ color: hexCode });
} else {
const role = await interaction.guild!.roles.create({
color: hexCode,
name: `${interaction.user.username}!`,
position: 10,
});
await memberRoles.add(role);
}
return interaction.reply({
content: roleToUpdate ? "Role updated" : "Role created",
ephemeral: true,
});
},
};
function validateHexCode(hexCode: string) {
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(hexCode);
}

View File

@ -1,51 +0,0 @@
import { questionCommand } from "./q";
import { lfgRoleCommand } from "./lfg";
import { accessCommand } from "./access";
import { plusCommand } from "./plus";
import { updateAllCommand } from "./updateall";
import { pingRolesCommand } from "./pings";
import { colorCommand } from "./color";
import { loginCommand, updateProfileCommand } from "./login";
import type { BotCommand } from "../types";
export const commands = [
questionCommand,
lfgRoleCommand,
accessCommand,
plusCommand,
updateAllCommand,
pingRolesCommand,
colorCommand,
loginCommand,
updateProfileCommand,
];
export const commandsMap = Object.fromEntries(commands.map((c) => [c.name, c]));
interface CommandsPerGuilds {
guildId: string;
commands: BotCommand[];
}
export function commandsPerGuild() {
const result: CommandsPerGuilds[] = [];
for (const command of commands) {
for (const guildId of command.guilds) {
const guildsCommands = result.find((c) => c.guildId === guildId);
if (!guildsCommands) {
const guildsCommands: CommandsPerGuilds = {
guildId,
commands: [],
};
guildsCommands.commands.push(command);
result.push(guildsCommands);
continue;
}
guildsCommands.commands.push(command);
}
}
return result;
}

View File

@ -1,49 +0,0 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import type { GuildMemberRoleManager } from "discord.js";
import ids from "../ids";
import type { BotCommand } from "../types";
const COMMAND_NAME = "lfg";
const ACTION_ARG = "action";
export const lfgRoleCommand: BotCommand = {
guilds: [ids.guilds.sro],
name: COMMAND_NAME,
builder: new SlashCommandBuilder()
.setName(COMMAND_NAME)
.setDescription(
"Turn on/off pings when people are looking for someone to play with",
)
.addStringOption((option) =>
option
.setName(ACTION_ARG)
.setDescription("Add or remove the role?")
.setRequired(true)
.addChoices(
{
name: "add",
value: "add",
},
{
name: "remove",
value: "remove",
},
),
),
execute: async ({ interaction }) => {
const roleBeingAdded = interaction.options.getString(ACTION_ARG) === "add";
const commandUserRoles = interaction.member
?.roles as GuildMemberRoleManager;
if (roleBeingAdded) {
await commandUserRoles.add(ids.roles.sroLfg);
} else {
await commandUserRoles.remove(ids.roles.sroLfg);
}
return interaction.reply({
content: roleBeingAdded ? "Role added" : "Role removed",
ephemeral: true,
});
},
};

View File

@ -1,96 +0,0 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import type { CommandInteraction, User } from "discord.js";
import ids from "../ids";
import type { BotCommand } from "../types";
import { sendouInkFetch } from "../utils";
const LOGIN_COMMAND_NAME = "login";
const UPDATE_PROFILE_COMMAND_NAME = "update-profile";
export const loginCommand: BotCommand = {
guilds: [ids.guilds.plusServer, ids.guilds.sendou],
name: LOGIN_COMMAND_NAME,
builder: new SlashCommandBuilder()
.setName(LOGIN_COMMAND_NAME)
.setDescription("Get log in link for sendou.ink"),
execute: async ({ interaction }) => {
await execute(interaction, false);
},
};
export const updateProfileCommand: BotCommand = {
guilds: [ids.guilds.plusServer, ids.guilds.sendou],
name: UPDATE_PROFILE_COMMAND_NAME,
builder: new SlashCommandBuilder()
.setName(UPDATE_PROFILE_COMMAND_NAME)
.setDescription("Update your username and profile picture on sendou.ink"),
execute: async ({ interaction }) => {
await execute(interaction, true);
},
};
async function execute(
interaction: CommandInteraction<any>,
updateOnly: boolean,
) {
const user = interaction.member?.user as User;
if (!user) {
return interaction.reply({
content: "Something went wrong",
});
}
const hasUniqueUsername = user.discriminator === "0";
const discordName = hasUniqueUsername ? user.globalName : user.username;
const discordUniqueName = hasUniqueUsername ? user.username : null;
if (!discordName || !discordUniqueName) {
return interaction.reply({
content:
"Can't do this with an account that is missing the new kind of Discord username",
});
}
const searchParams = new URLSearchParams(
user.avatar
? {
discordAvatar: user.avatar,
discordId: user.id,
discordName,
discordUniqueName,
updateOnly: String(updateOnly),
}
: {
discordId: user.id,
discordName,
discordUniqueName,
updateOnly: String(updateOnly),
},
);
const response = await sendouInkFetch(`/auth/create-link?${searchParams}`, {
method: "post",
});
if (updateOnly) {
if (!response.ok) {
return interaction.reply({
content: "Something went wrong when updating",
ephemeral: true,
});
}
return interaction.reply({
content: "Updated your profile on sendou.ink",
ephemeral: true,
});
}
const { code } = await response.json();
return interaction.reply({
content:
"Use the link below to log in to sendou.ink. It's active for 10 minutes. ⚠️ Don't share this link with others as it will allow them to log in to your account.\n\n[log in link](https://sendou.ink/auth/login?code=${code})",
ephemeral: true,
});
}

View File

@ -1,69 +0,0 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import type { GuildMemberRoleManager } from "discord.js";
import ids from "../ids";
import type { BotCommand } from "../types";
const COMMAND_NAME = "pings";
const TIER_ARG = "tier";
const ACTION_ARG = "action";
export const pingRolesCommand: BotCommand = {
guilds: [ids.guilds.plusServer],
name: COMMAND_NAME,
builder: new SlashCommandBuilder()
.setName(COMMAND_NAME)
.setDescription(
"Turn on/off pings when people are looking for someone to play with",
)
.addStringOption((option) =>
option
.setName(TIER_ARG)
.setDescription("+2 or +3?")
.setRequired(true)
.addChoices(
{
name: "+2",
value: "+2",
},
{
name: "+3",
value: "+3",
},
),
)
.addStringOption((option) =>
option
.setName(ACTION_ARG)
.setDescription("Add or remove the role?")
.setRequired(true)
.addChoices(
{
name: "add",
value: "add",
},
{
name: "remove",
value: "remove",
},
),
),
execute: async ({ interaction }) => {
const roleBeingAdded = interaction.options.getString(ACTION_ARG) === "add";
const tier = interaction.options.getString(TIER_ARG);
const role =
tier === "+2" ? ids.roles.plusTwoPings : ids.roles.plusThreePings;
const commandUserRoles = interaction.member
?.roles as GuildMemberRoleManager;
if (roleBeingAdded) {
await commandUserRoles.add(role);
} else {
await commandUserRoles.remove(role);
}
return interaction.reply({
content: `${tier} pings role ${roleBeingAdded ? "added" : "removed"}`,
ephemeral: true,
});
},
};

View File

@ -1,85 +0,0 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import { Client, GuildMember, Role } from "discord.js";
import invariant from "tiny-invariant";
import ids from "../ids";
import type { BotCommand } from "../types";
import { plusTierToRoleId, usersWithAccess } from "../utils";
const COMMAND_NAME = "plus";
const ACTION_ARG = "dry";
export const plusCommand: BotCommand = {
guilds: [ids.guilds.adminServer],
name: COMMAND_NAME,
builder: new SlashCommandBuilder()
.setName(COMMAND_NAME)
.setDescription("Remove roles from Plus Server from people who lost access")
.addBooleanOption((option) =>
option
.setName(ACTION_ARG)
.setDescription("If true doesn't remove or give roles")
.setRequired(true),
),
// @ts-expect-error TODO: fix. Library doesn't seem to extract API Message type so I could fix this error?
execute: async ({ interaction, client }) => {
const isDryRun = interaction.options.getBoolean(ACTION_ARG);
await interaction.deferReply({ ephemeral: true });
const { users } = await usersWithAccess();
let removed = 0;
let added = 0;
for (const { member, roleIds } of await membersWithPlusRoles(client)) {
const userMemberStatus = users[member.id];
const roleIdToGive = plusTierToRoleId(userMemberStatus);
const roleIdsToRemove = roleIds.filter((id) => id !== roleIdToGive);
for (const idToRemove of roleIdsToRemove) {
if (!isDryRun) await member.roles.remove(idToRemove);
removed++;
}
if (roleIdToGive && !roleIds.includes(roleIdToGive)) {
if (!isDryRun) await member.roles.add(roleIdToGive);
added++;
}
}
return interaction.editReply(`Removed ${removed} roles, gave ${added}.`);
},
};
async function membersWithPlusRoles(client: Client<boolean>) {
const plusServer = client.guilds.cache.find(
(g) => g.id === ids.guilds.plusServer,
);
invariant(plusServer);
const plusOne = await plusServer.roles.fetch(ids.roles.plusOne);
const plusTwo = await plusServer.roles.fetch(ids.roles.plusTwo);
const plusThree = await plusServer.roles.fetch(ids.roles.plusThree);
const memberIdToRoles = new Map<
string,
{ member: GuildMember; roleIds: string[] }
>();
for (const role of [plusOne, plusTwo, plusThree]) {
invariant(role);
for (const [, member] of role.members) {
const value = memberIdToRoles.get(member.id) ?? { member, roleIds: [] };
value.roleIds.push(role.id);
memberIdToRoles.set(member.id, value);
}
}
return Array.from(memberIdToRoles.values()).map(({ roleIds, member }) => ({
roleIds,
member,
}));
}

View File

@ -1,81 +0,0 @@
{
"sg": {
"seeking": [
"https://cdn.discordapp.com/attachments/865925063731445760/865925984065552404/SG_NT_Seeking.png",
"https://cdn.discordapp.com/attachments/865925063731445760/865926005776056360/SG_HT_Seeking.png"
],
"grillers": [
"https://cdn.discordapp.com/attachments/951441475034558484/951441708745388062/SG_NT_Grillers.png",
"https://cdn.discordapp.com/attachments/951441475034558484/951441742283018291/SG_HT_Grillers.png"
],
"rush": [
"https://cdn.discordapp.com/attachments/951442362775765022/951442493927485450/SG_NT_Rush.png",
"https://cdn.discordapp.com/attachments/951442362775765022/951442573971578880/SG_HT_Rush.png"
]
},
"mb": {
"seeking": [
"https://cdn.discordapp.com/attachments/865925063731445760/865926250963140628/MB_NT_Seeking.png",
"https://cdn.discordapp.com/attachments/865925063731445760/865926264409030696/MB_HT_Seeking.png"
],
"grillers": [
"https://cdn.discordapp.com/attachments/951441475034558484/951441787573141534/MB_NT_Grillers.png",
"https://cdn.discordapp.com/attachments/951441475034558484/951441828299817020/MB_HT_Grillers.png"
],
"rush": [
"https://cdn.discordapp.com/attachments/951442362775765022/951442753563291688/MB_NT_Rush.png",
"https://cdn.discordapp.com/attachments/951442362775765022/951442772345356288/MB_HT_Rush.png"
]
},
"lo": {
"seeking": [
"https://cdn.discordapp.com/attachments/865925063731445760/865926336199655424/LO_NT_Seeking.png",
"https://cdn.discordapp.com/attachments/865925063731445760/865926346545954836/LO_HT_Seeking.png"
],
"grillers": [
"https://cdn.discordapp.com/attachments/951441475034558484/951441880841854986/LO_NT_Grillers.png",
"https://cdn.discordapp.com/attachments/951441475034558484/951441897052864532/LO_HT_Grillers.png"
],
"rush": [
"https://cdn.discordapp.com/attachments/951442362775765022/951442800166174782/LO_NT_Rush.png",
"https://cdn.discordapp.com/attachments/951442362775765022/951442813596360714/LO_HT_Rush.png"
]
},
"ss": {
"seeking": [
"https://cdn.discordapp.com/attachments/865925063731445760/865926389223784468/SS_NT_Seeking.png",
"https://cdn.discordapp.com/attachments/865925063731445760/865926397561798656/SS_HT_Seeking.png"
],
"grillers": [
"https://cdn.discordapp.com/attachments/951441475034558484/951441959510233118/SS_NT_Grillers.png",
"https://cdn.discordapp.com/attachments/951441475034558484/951441974198698005/SS_HT_Grillers.png"
],
"rush": [
"https://cdn.discordapp.com/attachments/951442362775765022/951442852485935164/SS_NT_Rush.png",
"https://cdn.discordapp.com/attachments/951442362775765022/951442864305479710/SS_HT_Rush.png"
]
},
"ap": {
"seeking": [
"https://cdn.discordapp.com/attachments/865925063731445760/865926537629663242/AP_NT_Seeking.png",
"https://cdn.discordapp.com/attachments/865925063731445760/865926546635620382/AP_HT_Seeking.png"
],
"grillers": [
"https://cdn.discordapp.com/attachments/951441475034558484/951442012626878514/AP_NT_Grillers.png",
"https://cdn.discordapp.com/attachments/951441475034558484/951442021246201906/AP_HT_Grillers.png"
],
"rush": [
"https://cdn.discordapp.com/attachments/951442362775765022/951442890721206312/AP_NT_Rush.png",
"https://cdn.discordapp.com/attachments/951442362775765022/951442901219561472/AP_HT_Rush.png"
]
},
"fundamentals": "https://docs.google.com/document/d/1WTDiKSzV3SaD8Sn7wWlGreE2Y4Y7UrOv3SSv7qtzvcg/edit?usp=sharing",
"fundamentals2": "https://youtu.be/BvGbmLO94Po",
"advanced": "https://docs.google.com/document/d/16wcdlxqwqBvOnXIviB_ig0FS5_a96DKwYUIiGh6YHa8/edit?usp=sharing",
"spawns": "https://cdn.discordapp.com/attachments/836884751922757652/836886520250105886/SR_Time_Interval.png",
"ballpoint": "https://youtu.be/oSf39Yn0wOU",
"explo": "https://youtu.be/1qSW0_fA_L4",
"dynamo": "https://youtu.be/GrCCAI8giKc",
"bamboo": "https://youtu.be/yptwgqIoYiI",
"hydra": "https://youtu.be/_YduyOTVoLk"
}

View File

@ -1,106 +0,0 @@
import type { BotCommand } from "../types";
import ids from "../ids";
import { SlashCommandBuilder } from "@discordjs/builders";
import groupedLinks from "./q-data.json";
const COMMAND_NAME = "q";
const WAVE_ARG = "wave";
type Wave = "seeking" | "grillers" | "rush";
export const questionCommand: BotCommand = {
name: COMMAND_NAME,
guilds: [ids.guilds.sro],
builder: addSubcommands(
new SlashCommandBuilder()
.setName(COMMAND_NAME)
.setDescription("Find a link"),
),
execute: async ({ interaction }) => {
let value: string | string[] | Record<Wave, string | string[]> =
groupedLinks[
interaction.options.getSubcommand() as keyof typeof groupedLinks
];
if (typeof value === "string") {
return interaction.reply({ content: toContent(value), ephemeral: true });
}
value = value[interaction.options.getString(WAVE_ARG) as Wave];
if (!value) {
throw new Error(
`Unknown combination: ${interaction.options.getSubcommand()} + ${value}`,
);
}
return interaction.reply({
content: toContent(value as any),
ephemeral: true,
});
},
};
function addSubcommands(builder: SlashCommandBuilder) {
for (const [key, value] of Object.entries(groupedLinks)) {
builder.addSubcommand((subcommand) => {
const result = subcommand
.setName(key)
.setDescription(keyToDescription(key));
if (typeof value !== "string") {
result.addStringOption((option) =>
option
.setName(WAVE_ARG)
.setDescription("Type of map")
.setRequired(true)
.addChoices(
...Object.keys(value).map((key) => ({
name: key,
value: key,
})),
),
);
}
return result;
});
}
return builder;
}
function toContent(value: string | string[]) {
if (typeof value === "string") return value;
return value.join("\n");
}
function keyToDescription(key: string) {
const descriptions = new Map([
["sg", "Spawning Grounds maps"],
["mb", "Marooner's Bay maps"],
["lo", "Lost Outpost maps"],
["ss", "Salmonid Smokeyard maps"],
["ap", "Ruins of Ark Polaris maps"],
[
"fundamentals",
"Written guide going over all the Overfishing fundamentals",
],
[
"fundamentals2",
"Live video that goes into some Overfishing fundamentals",
],
["advanced", "Written guide going over advanced Overfishing strategies"],
["spawns", "Table showing spawn timers"],
["ballpoint", "Video guide explaining the basics of Ballpoint in SR"],
["explo", "Video guide explaining the basics of Explosher in SR"],
["dynamo", "Video guide explaining the basics of Dynamo in SR"],
["bamboo", "Video guide explaining the basics of Bamboo in SR"],
["hydra", "Video guide explaining the basics of Hydra in SR"],
]);
const result = descriptions.get(key);
if (!result) throw new Error(`Unknown subcommand: ${key}`);
return result;
}

View File

@ -1,68 +0,0 @@
import { SlashCommandBuilder } from "@discordjs/builders";
import { Client } from "discord.js";
import invariant from "tiny-invariant";
import type { User } from "../../app/db/types";
import ids from "../ids";
import type { BotCommand } from "../types";
import { sendouInkFetch } from "../utils";
const COMMAND_NAME = "updateall";
const guildsToCrawlForUpdates = [ids.guilds.plusServer, ids.guilds.sendou];
export const updateAllCommand: BotCommand = {
guilds: [ids.guilds.adminServer],
name: COMMAND_NAME,
builder: new SlashCommandBuilder()
.setName(COMMAND_NAME)
.setDescription("Update sendou.ink usernames and avatars"),
// @ts-expect-error TODO: fix. Library doesn't seem to extract API Message type so I could fix this error?
execute: async ({ interaction, client }) => {
await interaction.deferReply({ ephemeral: true });
const userUpdates = await getUsersToUpdate(client);
const response = await sendouInkFetch("/users", {
method: "post",
body: JSON.stringify(userUpdates),
});
if (!response.ok) {
return interaction.editReply(
`Update failed with status code ${response.status}`,
);
}
return interaction.editReply(`Sent ${userUpdates.length} users`);
},
};
async function getUsersToUpdate(client: Client<boolean>) {
const usersSeen = new Set<string>();
const userUpdates: Array<{
discordId: string;
discordAvatar: string | null;
discordName: string | null;
discordUniqueName: string | null;
}> = [];
for (const guildId of guildsToCrawlForUpdates) {
const guild = client.guilds.cache.find((g) => g.id === guildId);
invariant(guild);
for (const [, { user }] of await guild.members.fetch()) {
if (usersSeen.has(user.id)) continue;
const hasUniqueUsername = user.discriminator === "0";
userUpdates.push({
discordId: user.id,
discordAvatar: user.avatar,
discordName: hasUniqueUsername ? user.globalName : user.username,
discordUniqueName: hasUniqueUsername ? user.username : null,
});
usersSeen.add(user.id);
}
}
return userUpdates;
}

View File

@ -1,46 +0,0 @@
import "dotenv/config";
import { REST } from "@discordjs/rest";
import { Routes } from "discord-api-types/v9";
import invariant from "tiny-invariant";
import { commands, commandsPerGuild } from "./commands";
invariant(process.env["BOT_TOKEN"], "DISCORD_TOKEN must be set");
invariant(process.env["BOT_ID"], "BOT_ID must be set");
const rest = new REST({ version: "9" }).setToken(process.env["BOT_TOKEN"]);
if (process.env.NODE_ENV !== "production") {
invariant(process.env["TEST_GUILD_ID"], "TEST_GUILD_ID must be set");
const serializedCommands = commands.map((cmd) => cmd.builder.toJSON());
rest
.put(
Routes.applicationGuildCommands(
process.env["BOT_ID"],
process.env["TEST_GUILD_ID"],
),
{ body: serializedCommands }, // TODO: divide by guild
)
// eslint-disable-next-line no-console
.then(() => console.log("Successfully registered application commands."))
.catch(console.error);
} else {
for (const guildsCommands of commandsPerGuild()) {
const serializedCommands = guildsCommands.commands.map((cmd) =>
cmd.builder.toJSON(),
);
rest
.put(
Routes.applicationGuildCommands(
process.env["BOT_ID"],
guildsCommands.guildId,
),
{ body: serializedCommands },
)
.catch(console.error);
}
// eslint-disable-next-line no-console
console.log("All commands deployed");
}

View File

@ -1,9 +0,0 @@
import { GuildMember } from "discord.js";
import { givePlusRoleToMember } from "./commands/access";
import ids from "./ids";
export async function handleMemberJoin(member: GuildMember) {
if (member.guild.id !== ids.guilds.plusServer) return;
await givePlusRoleToMember({ member });
}

View File

@ -1,36 +0,0 @@
const ids = {
users: {
admin: "79237403620945920",
},
roles: {
sroLfg: "749589703371653158",
sroCoach: "807258345223356486",
plusTwoPings: "678602424314888224",
plusThreePings: "818060819811663872",
plusOne: "657613334249734165",
plusTwo: "678169134416265260",
plusThree: "815267885601521696",
// TESTING VALUES
// plusTwoPings: "1017077667276783657",
// plusThreePings: "1017077722813575290",
// plusOne: "992852036334911638",
// plusTwo: "992852055251234936",
// plusThree: "992852066403893258",
patreonSupporter: "787281384909045760",
patreonSupporterPlus: "787281624152801350",
patreonSupporterPlusPlus: "787281663138725928",
},
channels: {
// Salmon Run // Plus Server
bot: ["746130457455886337", "808995745768013854"],
sroLfg: "745036737943699546",
},
guilds: {
sro: "737359708276654121",
plusServer: "351828612208525333",
sendou: "299182152161951744",
adminServer: "986325847323582474",
},
} as const;
export default ids;

View File

@ -1,39 +0,0 @@
import { Client, GatewayIntentBits } from "discord.js";
import "dotenv/config";
import invariant from "tiny-invariant";
import { commandsMap } from "./commands";
import { handleMemberJoin } from "./handleMemberJoin";
invariant(process.env["BOT_TOKEN"], "DISCORD_TOKEN must be set");
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],
});
client.once("ready", () => {
// eslint-disable-next-line no-console
console.log("Ready!");
});
client.on("guildMemberAdd", handleMemberJoin);
client.on("interactionCreate", async (interaction) => {
if (!interaction.isCommand()) return;
const { commandName } = interaction;
const command = commandsMap[commandName];
if (!command) {
throw new Error(
`I don't know how to handle the command called "${commandName}"`,
);
}
try {
await command.execute({ interaction, client });
} catch (e) {
console.error(e);
}
});
client.login(process.env["BOT_TOKEN"]).catch((err) => console.error(err));

View File

@ -1,873 +0,0 @@
{
"name": "lohi",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "lohi",
"dependencies": {
"@discordjs/rest": "^2.0.1",
"discord-api-types": "^0.37.56",
"discord.js": "^14.13.0",
"dotenv": "^16.3.1",
"tiny-invariant": "^1.3.1",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.2.2"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@discordjs/builders": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.6.5.tgz",
"integrity": "sha512-SdweyCs/+mHj+PNhGLLle7RrRFX9ZAhzynHahMCLqp5Zeq7np7XC6/mgzHc79QoVlQ1zZtOkTTiJpOZu5V8Ufg==",
"dependencies": {
"@discordjs/formatters": "^0.3.2",
"@discordjs/util": "^1.0.1",
"@sapphire/shapeshift": "^3.9.2",
"discord-api-types": "0.37.50",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.3",
"tslib": "^2.6.1"
},
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/builders/node_modules/discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
},
"node_modules/@discordjs/collection": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
"integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/formatters": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.2.tgz",
"integrity": "sha512-lE++JZK8LSSDRM5nLjhuvWhGuKiXqu+JZ/DsOR89DVVia3z9fdCJVcHF2W/1Zxgq0re7kCzmAJlCMMX3tetKpA==",
"dependencies": {
"discord-api-types": "0.37.50"
},
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/formatters/node_modules/discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
},
"node_modules/@discordjs/rest": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.0.1.tgz",
"integrity": "sha512-/eWAdDRvwX/rIE2tuQUmKaxmWeHmGealttIzGzlYfI4+a7y9b6ZoMp8BG/jaohs8D8iEnCNYaZiOFLVFLQb8Zg==",
"dependencies": {
"@discordjs/collection": "^1.5.3",
"@discordjs/util": "^1.0.1",
"@sapphire/async-queue": "^1.5.0",
"@sapphire/snowflake": "^3.5.1",
"@vladfrangu/async_event_emitter": "^2.2.2",
"discord-api-types": "0.37.50",
"magic-bytes.js": "^1.0.15",
"tslib": "^2.6.1",
"undici": "5.22.1"
},
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/rest/node_modules/discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
},
"node_modules/@discordjs/util": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.1.tgz",
"integrity": "sha512-d0N2yCxB8r4bn00/hvFZwM7goDcUhtViC5un4hPj73Ba4yrChLSJD8fy7Ps5jpTLg1fE9n4K0xBLc1y9WGwSsA==",
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/ws": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.1.tgz",
"integrity": "sha512-avvAolBqN3yrSvdBPcJ/0j2g42ABzrv3PEL76e3YTp2WYMGH7cuspkjfSyNWaqYl1J+669dlLp+YFMxSVQyS5g==",
"dependencies": {
"@discordjs/collection": "^1.5.3",
"@discordjs/rest": "^2.0.1",
"@discordjs/util": "^1.0.1",
"@sapphire/async-queue": "^1.5.0",
"@types/ws": "^8.5.5",
"@vladfrangu/async_event_emitter": "^2.2.2",
"discord-api-types": "0.37.50",
"tslib": "^2.6.1",
"ws": "^8.13.0"
},
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/ws/node_modules/discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz",
"integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.13",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz",
"integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@sapphire/async-queue": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
"integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/shapeshift": {
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.2.tgz",
"integrity": "sha512-YRbCXWy969oGIdqR/wha62eX8GNHsvyYi0Rfd4rNW6tSVVa8p0ELiMEuOH/k8rgtvRoM+EMV7Csqz77YdwiDpA==",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/snowflake": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz",
"integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA=="
},
"node_modules/@tsconfig/node12": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.10.tgz",
"integrity": "sha512-N+srakvPaYMGkwjNDx3ASx65Zl3QG8dJgVtIB+YMOkucU+zctlv/hdP5250VKdDHSDoW9PFZoCqbqNcAPjCjXA=="
},
"node_modules/@tsconfig/node14": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.2.tgz",
"integrity": "sha512-YwrUA5ysDXHFYfL0Xed9x3sNS4P+aKlCOnnbqUa2E5HdQshHFleCJVrj1PlGTb4GgFUCDyte1v3JWLy2sz8Oqg=="
},
"node_modules/@tsconfig/node16": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ=="
},
"node_modules/@types/node": {
"version": "17.0.42",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.42.tgz",
"integrity": "sha512-Q5BPGyGKcvQgAMbsr7qEGN/kIPN6zZecYYABeTDBizOsau+2NMdSVTar9UQw21A2+JyA2KRNDYaYrPB0Rpk2oQ=="
},
"node_modules/@types/ws": {
"version": "8.5.5",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz",
"integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@vladfrangu/async_event_emitter": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz",
"integrity": "sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/acorn": {
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-walk": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/discord-api-types": {
"version": "0.37.56",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.56.tgz",
"integrity": "sha512-Ih3wj0ZTaQxaJRqUEXHMIXfXB86bwMGC0wc2nNsyCJqeo3lC4qnxXtFIsC+IGI46+dSIinuayCAZ6sLEEM02Bw=="
},
"node_modules/discord.js": {
"version": "14.13.0",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.13.0.tgz",
"integrity": "sha512-Kufdvg7fpyTEwANGy9x7i4od4yu5c6gVddGi5CKm4Y5a6sF0VBODObI3o0Bh7TGCj0LfNT8Qp8z04wnLFzgnbA==",
"dependencies": {
"@discordjs/builders": "^1.6.5",
"@discordjs/collection": "^1.5.3",
"@discordjs/formatters": "^0.3.2",
"@discordjs/rest": "^2.0.1",
"@discordjs/util": "^1.0.1",
"@discordjs/ws": "^1.0.1",
"@sapphire/snowflake": "^3.5.1",
"@types/ws": "^8.5.5",
"discord-api-types": "0.37.50",
"fast-deep-equal": "^3.1.3",
"lodash.snakecase": "^4.1.1",
"tslib": "^2.6.1",
"undici": "5.22.1",
"ws": "^8.13.0"
},
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/discord.js/node_modules/discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
},
"node_modules/dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.snakecase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
},
"node_modules/magic-bytes.js": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.0.15.tgz",
"integrity": "sha512-bpRmwbRHqongRhA+mXzbLWjVy7ylqmfMBYaQkSs6pac0z6hBTvsgrH0r4FBYd/UYVJBmS6Rp/O+oCCQVLzKV1g=="
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"node_modules/minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"engines": {
"node": ">=4"
}
},
"node_modules/tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
},
"node_modules/ts-mixer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
"integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
},
"node_modules/ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"dependencies": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici": {
"version": "5.22.1",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz",
"integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==",
"dependencies": {
"busboy": "^1.6.0"
},
"engines": {
"node": ">=14.0"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
},
"node_modules/ws": {
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.0.tgz",
"integrity": "sha512-WR0RJE9Ehsio6U4TuM+LmunEsjQ5ncHlw4sn9ihD6RoJKZrVyH9FWV3dmnwu8B2aNib1OvG2X6adUCyFpQyWcg==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"engines": {
"node": ">=6"
}
}
},
"dependencies": {
"@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"requires": {
"@jridgewell/trace-mapping": "0.3.9"
}
},
"@discordjs/builders": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.6.5.tgz",
"integrity": "sha512-SdweyCs/+mHj+PNhGLLle7RrRFX9ZAhzynHahMCLqp5Zeq7np7XC6/mgzHc79QoVlQ1zZtOkTTiJpOZu5V8Ufg==",
"requires": {
"@discordjs/formatters": "^0.3.2",
"@discordjs/util": "^1.0.1",
"@sapphire/shapeshift": "^3.9.2",
"discord-api-types": "0.37.50",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.3",
"tslib": "^2.6.1"
},
"dependencies": {
"discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
}
}
},
"@discordjs/collection": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
"integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ=="
},
"@discordjs/formatters": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.2.tgz",
"integrity": "sha512-lE++JZK8LSSDRM5nLjhuvWhGuKiXqu+JZ/DsOR89DVVia3z9fdCJVcHF2W/1Zxgq0re7kCzmAJlCMMX3tetKpA==",
"requires": {
"discord-api-types": "0.37.50"
},
"dependencies": {
"discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
}
}
},
"@discordjs/rest": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.0.1.tgz",
"integrity": "sha512-/eWAdDRvwX/rIE2tuQUmKaxmWeHmGealttIzGzlYfI4+a7y9b6ZoMp8BG/jaohs8D8iEnCNYaZiOFLVFLQb8Zg==",
"requires": {
"@discordjs/collection": "^1.5.3",
"@discordjs/util": "^1.0.1",
"@sapphire/async-queue": "^1.5.0",
"@sapphire/snowflake": "^3.5.1",
"@vladfrangu/async_event_emitter": "^2.2.2",
"discord-api-types": "0.37.50",
"magic-bytes.js": "^1.0.15",
"tslib": "^2.6.1",
"undici": "5.22.1"
},
"dependencies": {
"discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
}
}
},
"@discordjs/util": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.1.tgz",
"integrity": "sha512-d0N2yCxB8r4bn00/hvFZwM7goDcUhtViC5un4hPj73Ba4yrChLSJD8fy7Ps5jpTLg1fE9n4K0xBLc1y9WGwSsA=="
},
"@discordjs/ws": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.1.tgz",
"integrity": "sha512-avvAolBqN3yrSvdBPcJ/0j2g42ABzrv3PEL76e3YTp2WYMGH7cuspkjfSyNWaqYl1J+669dlLp+YFMxSVQyS5g==",
"requires": {
"@discordjs/collection": "^1.5.3",
"@discordjs/rest": "^2.0.1",
"@discordjs/util": "^1.0.1",
"@sapphire/async-queue": "^1.5.0",
"@types/ws": "^8.5.5",
"@vladfrangu/async_event_emitter": "^2.2.2",
"discord-api-types": "0.37.50",
"tslib": "^2.6.1",
"ws": "^8.13.0"
},
"dependencies": {
"discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
}
}
},
"@jridgewell/resolve-uri": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz",
"integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA=="
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.13",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz",
"integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w=="
},
"@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@sapphire/async-queue": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
"integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA=="
},
"@sapphire/shapeshift": {
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.2.tgz",
"integrity": "sha512-YRbCXWy969oGIdqR/wha62eX8GNHsvyYi0Rfd4rNW6tSVVa8p0ELiMEuOH/k8rgtvRoM+EMV7Csqz77YdwiDpA==",
"requires": {
"fast-deep-equal": "^3.1.3",
"lodash": "^4.17.21"
}
},
"@sapphire/snowflake": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz",
"integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA=="
},
"@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA=="
},
"@tsconfig/node12": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.10.tgz",
"integrity": "sha512-N+srakvPaYMGkwjNDx3ASx65Zl3QG8dJgVtIB+YMOkucU+zctlv/hdP5250VKdDHSDoW9PFZoCqbqNcAPjCjXA=="
},
"@tsconfig/node14": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.2.tgz",
"integrity": "sha512-YwrUA5ysDXHFYfL0Xed9x3sNS4P+aKlCOnnbqUa2E5HdQshHFleCJVrj1PlGTb4GgFUCDyte1v3JWLy2sz8Oqg=="
},
"@tsconfig/node16": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ=="
},
"@types/node": {
"version": "17.0.42",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.42.tgz",
"integrity": "sha512-Q5BPGyGKcvQgAMbsr7qEGN/kIPN6zZecYYABeTDBizOsau+2NMdSVTar9UQw21A2+JyA2KRNDYaYrPB0Rpk2oQ=="
},
"@types/ws": {
"version": "8.5.5",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz",
"integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==",
"requires": {
"@types/node": "*"
}
},
"@vladfrangu/async_event_emitter": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz",
"integrity": "sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ=="
},
"acorn": {
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
},
"acorn-walk": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA=="
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
},
"busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"requires": {
"streamsearch": "^1.1.0"
}
},
"create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
},
"discord-api-types": {
"version": "0.37.56",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.56.tgz",
"integrity": "sha512-Ih3wj0ZTaQxaJRqUEXHMIXfXB86bwMGC0wc2nNsyCJqeo3lC4qnxXtFIsC+IGI46+dSIinuayCAZ6sLEEM02Bw=="
},
"discord.js": {
"version": "14.13.0",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.13.0.tgz",
"integrity": "sha512-Kufdvg7fpyTEwANGy9x7i4od4yu5c6gVddGi5CKm4Y5a6sF0VBODObI3o0Bh7TGCj0LfNT8Qp8z04wnLFzgnbA==",
"requires": {
"@discordjs/builders": "^1.6.5",
"@discordjs/collection": "^1.5.3",
"@discordjs/formatters": "^0.3.2",
"@discordjs/rest": "^2.0.1",
"@discordjs/util": "^1.0.1",
"@discordjs/ws": "^1.0.1",
"@sapphire/snowflake": "^3.5.1",
"@types/ws": "^8.5.5",
"discord-api-types": "0.37.50",
"fast-deep-equal": "^3.1.3",
"lodash.snakecase": "^4.1.1",
"tslib": "^2.6.1",
"undici": "5.22.1",
"ws": "^8.13.0"
},
"dependencies": {
"discord-api-types": {
"version": "0.37.50",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.50.tgz",
"integrity": "sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg=="
}
}
},
"dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ=="
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash.snakecase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
},
"magic-bytes.js": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.0.15.tgz",
"integrity": "sha512-bpRmwbRHqongRhA+mXzbLWjVy7ylqmfMBYaQkSs6pac0z6hBTvsgrH0r4FBYd/UYVJBmS6Rp/O+oCCQVLzKV1g=="
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="
},
"tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw=="
},
"ts-mixer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz",
"integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ=="
},
"ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
"requires": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
}
},
"tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"requires": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
}
},
"tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w=="
},
"undici": {
"version": "5.22.1",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz",
"integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==",
"requires": {
"busboy": "^1.6.0"
}
},
"v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
},
"ws": {
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.0.tgz",
"integrity": "sha512-WR0RJE9Ehsio6U4TuM+LmunEsjQ5ncHlw4sn9ihD6RoJKZrVyH9FWV3dmnwu8B2aNib1OvG2X6adUCyFpQyWcg==",
"requires": {}
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
}
}
}

View File

@ -1,19 +0,0 @@
{
"name": "lohi",
"scripts": {
"dev": "node --experimental-fetch --require ts-node/register --require tsconfig-paths/register index.ts",
"commands": "node --experimental-fetch --require ts-node/register --require tsconfig-paths/register deploy-commands.ts",
"test": "echo \"Error: no test specified\" && exit 1",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@discordjs/rest": "^2.0.1",
"discord-api-types": "^0.37.56",
"discord.js": "^14.13.0",
"dotenv": "^16.3.1",
"tiny-invariant": "^1.3.1",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.2.2"
}
}

View File

@ -1,6 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"isolatedModules": false
}
}

View File

@ -1,13 +0,0 @@
import type { SlashCommandBuilder } from "@discordjs/builders";
import type { Client, CommandInteraction } from "discord.js";
export interface BotCommand {
name: string;
/** ID's of guilds where this command will be set */
guilds: string[];
builder: Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">; // TODO: figure out correct type here
execute: (commandParameters: {
interaction: CommandInteraction;
client: Client;
}) => Promise<void>;
}

View File

@ -1,57 +0,0 @@
import invariant from "tiny-invariant";
import { LOHI_TOKEN_HEADER_NAME } from "~/constants";
import type { PlusListLoaderData } from "~/routes/plus/list";
import ids from "./ids";
export function sendouInkFetch(path: string, init?: RequestInit) {
invariant(process.env["SENDOU_INK_URL"], "SENDOU_INK_URL is not set");
invariant(process.env["LOHI_TOKEN"], "LOHI_TOKEN is not set");
return fetch(`${process.env["SENDOU_INK_URL"]}${path}`, {
headers: [[LOHI_TOKEN_HEADER_NAME, process.env["LOHI_TOKEN"]]],
...init,
});
}
export async function usersWithAccess(): Promise<PlusListLoaderData> {
const response = await sendouInkFetch("/plus/list");
if (!response.ok) {
throw new Error(
`Failed to fetch users. Response status was ${response.status}`,
);
}
return response.json();
}
export async function patronsFiveDollarsAndOver(): Promise<PlusListLoaderData> {
const response = await sendouInkFetch("/patrons");
if (!response.ok) {
throw new Error(
`Failed to fetch users. Response status was ${response.status}`,
);
}
return (await response.json()).filter((user) => user.patronTier >= 2);
}
const memberRoles = [
"",
ids.roles.plusOne,
ids.roles.plusTwo,
ids.roles.plusThree,
] as const;
export function plusTierToRoleId(tier?: number) {
if (!tier) return;
const result = memberRoles[tier ?? 0];
if (!result) return;
return result;
}
export function isPlusTierRoleId(id: string) {
return memberRoles.slice(1).includes(id as any);
}

View File

@ -1,6 +1,5 @@
{
"include": ["./types/remix.env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["discord-bot/*"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"module": "commonjs",