feat: last bit of polish
Some checks failed
Node.js CI / Lint and Test (pull_request) Failing after 37s

This commit is contained in:
2025-08-11 19:37:12 -07:00
parent eafb205f91
commit 0a2f75380e
8 changed files with 82 additions and 16 deletions

View File

@@ -5,7 +5,10 @@
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"lint": "eslint ./src --max-warnings 0",
"build": "prisma generate && tsc",
"start": "op run --env-file=./prod.env -- node ./prod/index.js",
"test": "echo 'No tests yet' && exit 0"
},
"keywords": [],
"author": "",

View File

@@ -8,6 +8,7 @@ import { getEntitlement } from "../modules/getEntitlement.js";
import { getSkuLimit } from "../modules/getSkuLimit.js";
import { sendUnentitledResponse } from "../modules/sendUnintitledResponse.js";
import { errorHandler } from "../utils/errorHandler.js";
import { logger } from "../utils/logger.js";
import type { Command } from "../interfaces/command.js";
/**
@@ -31,9 +32,21 @@ export const add: Command = async(lynira, interaction) => {
return;
}
const url = interaction.options.getString("url", true);
const slugExists = await lynira.db.links.findFirst({ where: {
const isValidUrl = /^https?:\/\/(?:[\da-z-]+\.)+[a-z]{2,}(?:\/\S*)?$/i.test(
url,
);
if (!isValidUrl) {
await logger.log("debug", `Provided URL did not match expected pattern: ${url}`);
await interaction.editReply({
content: "The provided URL is not valid. Please provide a valid URL.",
});
return;
}
const slugExists = await lynira.db.links.findFirst({
where: {
slug,
} });
},
});
if (slugExists) {
await interaction.editReply({
content: "This slug is already in use. Please choose a different one.",
@@ -63,6 +76,10 @@ export const add: Command = async(lynira, interaction) => {
await interaction.editReply({
content: `Short URL created. Visit <https://lynira.link/${slug}> to access it. Please note that for the safety of our users, you will not be able to edit this short link.`,
});
await logger.log(
"info",
`User ${interaction.user.id} created a short URL with slug "${slug}" pointing to "${url}".`,
);
} catch (error) {
await errorHandler(error, "add command");
await interaction.editReply({

View File

@@ -6,6 +6,7 @@
import { getEntitlement } from "../modules/getEntitlement.js";
import { sendUnentitledResponse } from "../modules/sendUnintitledResponse.js";
import { errorHandler } from "../utils/errorHandler.js";
import { logger } from "../utils/logger.js";
import type { Command } from "../interfaces/command.js";
/**
@@ -40,6 +41,7 @@ export const remove: Command = async(lynira, interaction) => {
await interaction.editReply({
content: `Short URL <https://lynira.link/${slug}> has been removed. Please note that for the safety of our users, this slug will not be available for reuse.`,
});
await logger.log("info", `User ${interaction.user.id} removed a short URL with slug "${slug}".`);
} catch (error) {
await errorHandler(error, "remove command");
await interaction.editReply({

View File

@@ -5,8 +5,8 @@
*/
import { PrismaClient } from "@prisma/client";
import { Client, Events, MessageFlags } from "discord.js";
import { handlers } from "./config/handlers.js";
import { Client, Events } from "discord.js";
import { processCommand } from "./modules/processCommand.js";
import { instantiateServer } from "./server/serve.js";
import { logger } from "./utils/logger.js";
import type { Lynira } from "./interfaces/lynira.js";
@@ -26,16 +26,11 @@ lynira.discord.on("error", (error) => {
lynira.discord.on(Events.InteractionCreate, (interaction) => {
if (interaction.isChatInputCommand()) {
void interaction.deferReply({
flags: [ MessageFlags.Ephemeral ],
});
const { commandName } = interaction;
// eslint-disable-next-line no-underscore-dangle -- Accessing private property for command handler.
void (handlers[commandName] ?? handlers._default)(lynira, interaction);
void processCommand(lynira, interaction);
}
});
await lynira.db.$connect();
await lynira.discord.login(process.env.DISCORD_TOKEN);
await lynira.discord.login(process.env.BOT_TOKEN);
instantiateServer(lynira);

View File

@@ -9,5 +9,6 @@ export enum SKU {
PERSONAL = "1404602103434973186",
PROFESSIONAL = "1404602584404328573",
BUSINESS = "1404602909370613931",
ENTERPRISE = "1404603245963513898",
ENTERPRISE = "1404653451224416317",
ORGANISATION = "1404603245963513898",
}

View File

@@ -4,8 +4,9 @@
* @author Naomi Carrigan
*/
import { type Entitlement, EntitlementType } from "discord.js";
import { SKU } from "../interfaces/skus.js";
import type { Lynira } from "../interfaces/lynira.js";
import type { Entitlement } from "discord.js";
/**
* Fetches an entitlement for a user. Only looks at the first ACTIVE entitlement.
@@ -17,6 +18,27 @@ export const getEntitlement = async(
lynira: Lynira,
userId: string,
): Promise<Entitlement | null> => {
if (userId === "465650873650118659") {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Mock entitlement for Naomi.
return {
applicationId: lynira.discord.application?.id ?? "",
consumed: false,
deleted: false,
endsAt: null,
endsTimestamp: null,
guild: null,
guildId: null,
id: "enterprise-entitlement-id",
isActive: () => {
return true;
},
skuId: SKU.ENTERPRISE,
startsAt: null,
startsTimestamp: null,
type: EntitlementType.ApplicationSubscription,
userId: "465650873650118659",
} as Entitlement;
}
const entitlements = await lynira.discord.application?.entitlements.fetch({
excludeDeleted: true,
excludeEnded: true,

View File

@@ -17,11 +17,14 @@ export const getSkuLimit = (sku: SKU | string): number => {
return 10;
}
if (sku === SKU.PROFESSIONAL) {
return 50;
return 25;
}
if (sku === SKU.BUSINESS) {
return 100;
}
if (sku === SKU.ORGANISATION) {
return 250;
}
if (sku === SKU.ENTERPRISE) {
return 1000;
}

View File

@@ -0,0 +1,23 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { MessageFlags } from "discord.js";
import { handlers } from "../config/handlers.js";
import type { Command } from "../interfaces/command.js";
/**
* Process a command interaction.
* @param lynira - The Lynira instance.
* @param interaction - The interaction to process.
*/
export const processCommand: Command = async(lynira, interaction) => {
await interaction.deferReply({
flags: [ MessageFlags.Ephemeral ],
});
const { commandName } = interaction;
// eslint-disable-next-line no-underscore-dangle -- Accessing private property for command handler.
await (handlers[commandName] ?? handlers._default)(lynira, interaction);
};