generated from nhcarrigan/template
feat: last bit of polish
Some checks failed
Node.js CI / Lint and Test (pull_request) Failing after 37s
Some checks failed
Node.js CI / Lint and Test (pull_request) Failing after 37s
This commit is contained in:
@@ -5,7 +5,10 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"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": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { getEntitlement } from "../modules/getEntitlement.js";
|
|||||||
import { getSkuLimit } from "../modules/getSkuLimit.js";
|
import { getSkuLimit } from "../modules/getSkuLimit.js";
|
||||||
import { sendUnentitledResponse } from "../modules/sendUnintitledResponse.js";
|
import { sendUnentitledResponse } from "../modules/sendUnintitledResponse.js";
|
||||||
import { errorHandler } from "../utils/errorHandler.js";
|
import { errorHandler } from "../utils/errorHandler.js";
|
||||||
|
import { logger } from "../utils/logger.js";
|
||||||
import type { Command } from "../interfaces/command.js";
|
import type { Command } from "../interfaces/command.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,9 +32,21 @@ export const add: Command = async(lynira, interaction) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const url = interaction.options.getString("url", true);
|
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,
|
slug,
|
||||||
} });
|
},
|
||||||
|
});
|
||||||
if (slugExists) {
|
if (slugExists) {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
content: "This slug is already in use. Please choose a different one.",
|
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({
|
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.`,
|
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) {
|
} catch (error) {
|
||||||
await errorHandler(error, "add command");
|
await errorHandler(error, "add command");
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import { getEntitlement } from "../modules/getEntitlement.js";
|
import { getEntitlement } from "../modules/getEntitlement.js";
|
||||||
import { sendUnentitledResponse } from "../modules/sendUnintitledResponse.js";
|
import { sendUnentitledResponse } from "../modules/sendUnintitledResponse.js";
|
||||||
import { errorHandler } from "../utils/errorHandler.js";
|
import { errorHandler } from "../utils/errorHandler.js";
|
||||||
|
import { logger } from "../utils/logger.js";
|
||||||
import type { Command } from "../interfaces/command.js";
|
import type { Command } from "../interfaces/command.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,6 +41,7 @@ export const remove: Command = async(lynira, interaction) => {
|
|||||||
await interaction.editReply({
|
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.`,
|
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) {
|
} catch (error) {
|
||||||
await errorHandler(error, "remove command");
|
await errorHandler(error, "remove command");
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
|
|||||||
13
src/index.ts
13
src/index.ts
@@ -5,8 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { PrismaClient } from "@prisma/client";
|
import { PrismaClient } from "@prisma/client";
|
||||||
import { Client, Events, MessageFlags } from "discord.js";
|
import { Client, Events } from "discord.js";
|
||||||
import { handlers } from "./config/handlers.js";
|
import { processCommand } from "./modules/processCommand.js";
|
||||||
import { instantiateServer } from "./server/serve.js";
|
import { instantiateServer } from "./server/serve.js";
|
||||||
import { logger } from "./utils/logger.js";
|
import { logger } from "./utils/logger.js";
|
||||||
import type { Lynira } from "./interfaces/lynira.js";
|
import type { Lynira } from "./interfaces/lynira.js";
|
||||||
@@ -26,16 +26,11 @@ lynira.discord.on("error", (error) => {
|
|||||||
|
|
||||||
lynira.discord.on(Events.InteractionCreate, (interaction) => {
|
lynira.discord.on(Events.InteractionCreate, (interaction) => {
|
||||||
if (interaction.isChatInputCommand()) {
|
if (interaction.isChatInputCommand()) {
|
||||||
void interaction.deferReply({
|
void processCommand(lynira, interaction);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await lynira.db.$connect();
|
await lynira.db.$connect();
|
||||||
await lynira.discord.login(process.env.DISCORD_TOKEN);
|
await lynira.discord.login(process.env.BOT_TOKEN);
|
||||||
|
|
||||||
instantiateServer(lynira);
|
instantiateServer(lynira);
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ export enum SKU {
|
|||||||
PERSONAL = "1404602103434973186",
|
PERSONAL = "1404602103434973186",
|
||||||
PROFESSIONAL = "1404602584404328573",
|
PROFESSIONAL = "1404602584404328573",
|
||||||
BUSINESS = "1404602909370613931",
|
BUSINESS = "1404602909370613931",
|
||||||
ENTERPRISE = "1404603245963513898",
|
ENTERPRISE = "1404653451224416317",
|
||||||
|
ORGANISATION = "1404603245963513898",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
* @author Naomi Carrigan
|
* @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 { Lynira } from "../interfaces/lynira.js";
|
||||||
import type { Entitlement } from "discord.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches an entitlement for a user. Only looks at the first ACTIVE entitlement.
|
* Fetches an entitlement for a user. Only looks at the first ACTIVE entitlement.
|
||||||
@@ -17,6 +18,27 @@ export const getEntitlement = async(
|
|||||||
lynira: Lynira,
|
lynira: Lynira,
|
||||||
userId: string,
|
userId: string,
|
||||||
): Promise<Entitlement | null> => {
|
): 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({
|
const entitlements = await lynira.discord.application?.entitlements.fetch({
|
||||||
excludeDeleted: true,
|
excludeDeleted: true,
|
||||||
excludeEnded: true,
|
excludeEnded: true,
|
||||||
|
|||||||
@@ -17,11 +17,14 @@ export const getSkuLimit = (sku: SKU | string): number => {
|
|||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
if (sku === SKU.PROFESSIONAL) {
|
if (sku === SKU.PROFESSIONAL) {
|
||||||
return 50;
|
return 25;
|
||||||
}
|
}
|
||||||
if (sku === SKU.BUSINESS) {
|
if (sku === SKU.BUSINESS) {
|
||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
if (sku === SKU.ORGANISATION) {
|
||||||
|
return 250;
|
||||||
|
}
|
||||||
if (sku === SKU.ENTERPRISE) {
|
if (sku === SKU.ENTERPRISE) {
|
||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/modules/processCommand.ts
Normal file
23
src/modules/processCommand.ts
Normal 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);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user