Skip to content

Commit

Permalink
Merge pull request #22 from tanguyMichardiere/develop
Browse files Browse the repository at this point in the history
deploy
  • Loading branch information
tanguyMichardiere authored Jan 24, 2025
2 parents a8f5264 + 70197ce commit e08b335
Show file tree
Hide file tree
Showing 29 changed files with 457 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
".env": ".env.*",
"package.json": "bun.lockb",
"package.json": "bun.lock",
"README.md": "LICENSE",
"tsconfig.json": "bunfig.toml, biome.jsonc, *.config.ts"
},
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM oven/bun AS builder

WORKDIR /home/bun/app

COPY package.json bun.lockb ./
COPY package.json bun.lock ./
COPY patches patches
RUN bun install --frozen-lockfile

Expand Down
6 changes: 5 additions & 1 deletion biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
"linter": {
"rules": {
"all": true,
"correctness": {
"useImportExtensions": "off"
},
"style": {
"useNamingConvention": "off"
},
"nursery": {
"all": true,
"useImportExtensions": "off",
"noEnum": "off",
"noSecrets": "off",
"useImportRestrictions": "off"
}
}
Expand Down
365 changes: 365 additions & 0 deletions bun.lock

Large diffs are not rendered by default.

Binary file removed bun.lockb
Binary file not shown.
33 changes: 16 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"private": true,
"scripts": {
"update-dependencies": "npm-check-updates -u && bun install && bun update",
"postupdate-dependencies": "biome format --write package.json",
"pregenerate-migration": "rm -f src/db/_migration.sql",
"generate-migration": "drizzle-kit generate --name migration",
"postgenerate-migration": "rm -rf src/db/meta",
Expand All @@ -17,25 +16,25 @@
"postbuild": "rm -f .*.bun-build"
},
"dependencies": {
"@discordjs/core": "1.2.0",
"@discordjs/rest": "2.3.0",
"@discordjs/ws": "1.1.1",
"@t3-oss/env-core": "0.10.1",
"drizzle-orm": "0.32.0",
"pino": "9.3.1",
"zod": "3.23.8"
"@discordjs/core": "2.0.1",
"@discordjs/rest": "2.4.2",
"@discordjs/ws": "2.0.1",
"@t3-oss/env-core": "0.11.1",
"drizzle-orm": "0.38.4",
"pino": "9.6.0",
"zod": "3.24.1"
},
"devDependencies": {
"@biomejs/biome": "1.8.3",
"@total-typescript/ts-reset": "0.5.1",
"@types/bun": "1.1.6",
"drizzle-kit": "0.23.0",
"knip": "5.26.0",
"npm-check-updates": "16.14.20",
"pino-pretty": "11.2.1",
"typescript": "5.5.3"
"@biomejs/biome": "1.9.4",
"@total-typescript/ts-reset": "0.6.1",
"@types/bun": "1.2.0",
"drizzle-kit": "0.30.2",
"knip": "5.43.3",
"npm-check-updates": "17.1.14",
"pino-pretty": "13.0.0",
"typescript": "5.7.3"
},
"patchedDependencies": {
"discord-api-types@0.37.83": "patches/discord-api-types@0.37.83.patch"
"discord-api-types@0.37.117": "patches/discord-api-types@0.37.117.patch"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/globals.d.ts b/globals.d.ts
index bd4145c11a6b431126526127d9df3f4e526e8d56..f4d10dd97ef55a2a2f9257eed8f04610513f3d04 100644
index f9d5d3d7fb9606a5aaec2234d4139b1d61eaf84c..8a938814c9d579f92150b8cf1e97d552f7cf5c30 100644
--- a/globals.d.ts
+++ b/globals.d.ts
@@ -1,7 +1,7 @@
Expand Down
8 changes: 4 additions & 4 deletions src/db/_migration.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CREATE TABLE `group_members` (
FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `group_members_idx` ON `group_members` (`member_id`,`group_id`);--> statement-breakpoint
CREATE TABLE `groups` (
`id` integer PRIMARY KEY NOT NULL,
`snowflake` text NOT NULL,
Expand All @@ -14,11 +15,13 @@ CREATE TABLE `groups` (
FOREIGN KEY (`guild_id`) REFERENCES `guilds`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `groups_snowflake_idx` ON `groups` (`snowflake`);--> statement-breakpoint
CREATE TABLE `guilds` (
`id` integer PRIMARY KEY NOT NULL,
`snowflake` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `guilds_snowflake_idx` ON `guilds` (`snowflake`);--> statement-breakpoint
CREATE TABLE `members` (
`id` integer PRIMARY KEY NOT NULL,
`user_id` integer NOT NULL,
Expand All @@ -28,13 +31,10 @@ CREATE TABLE `members` (
FOREIGN KEY (`guild_id`) REFERENCES `guilds`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `members_idx` ON `members` (`user_id`,`guild_id`);--> statement-breakpoint
CREATE TABLE `users` (
`id` integer PRIMARY KEY NOT NULL,
`snowflake` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `group_members_idx` ON `group_members` (`member_id`,`group_id`);--> statement-breakpoint
CREATE UNIQUE INDEX `groups_snowflake_idx` ON `groups` (`snowflake`);--> statement-breakpoint
CREATE UNIQUE INDEX `guilds_snowflake_idx` ON `guilds` (`snowflake`);--> statement-breakpoint
CREATE UNIQUE INDEX `members_idx` ON `members` (`user_id`,`guild_id`);--> statement-breakpoint
CREATE UNIQUE INDEX `users_snowflake_idx` ON `users` (`snowflake`);
6 changes: 3 additions & 3 deletions src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { Database } from "bun:sqlite";
import { drizzle } from "drizzle-orm/bun-sqlite";
import { logger } from "../logger";
// @ts-expect-error
import migrationFile from "./_migration.sql" with { type: "file" };
import migration from "./_migration.sql" with { type: "text" };
import { schema } from "./schema";

const sqlite = new Database();
sqlite.run("PRAGMA foreign_keys = ON;");
sqlite.run(await Bun.file(migrationFile).text());
sqlite.run(migration);
logger.debug("successfully applied the database migration");

export const db = drizzle(sqlite, {
schema,
logger: {
logQuery(query, params) {
logQuery(query, params): void {
let index = 0;
logger.debug(query.replaceAll("?", () => JSON.stringify(params[index++])));
},
Expand Down
1 change: 1 addition & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const env = createEnv({
ROLE_PREFIX: z.string().default("ps "),
ADMIN_ROLE_NAME: z.string().default("Admin Presta'sons"),
},
// biome-ignore lint/nursery/noProcessEnv: checked by t3env
runtimeEnv: process.env,
emptyStringAsUndefined: true,
});
14 changes: 8 additions & 6 deletions src/event-handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
import type { ManagerShardEventsMap } from "@discordjs/core";
import { type Db, db } from "../db";
import type { Db } from "../db";
import { db } from "../db";
import type { Logger } from "../logger";
import { logger } from "../logger";

type Opts = {
type Options = {
logEvent: boolean;
};

type ListenerOpts = {
type ListenerOptions = {
db: Db;
logger: Logger;
};

/** Create an event handler, with logging and error handling */
export function createEventHandler<K extends keyof ManagerShardEventsMap>(
eventName: K,
listener: (args: ManagerShardEventsMap[K][0], opts: ListenerOpts) => Promise<void>,
{ logEvent = true }: Partial<Opts> = {},
listener: (args: ManagerShardEventsMap[K][0], opts: ListenerOptions) => Promise<void>,
{ logEvent = true }: Partial<Options> = {},
): (args: ManagerShardEventsMap[K][0]) => Promise<void> {
const childLogger = logger.child({ eventName });
childLogger.info("registering a handler");
return async (args) => {
return async (args): Promise<void> => {
if (logEvent) {
logger.info({ event: args.data }, eventName);
}
try {
await db.transaction(async (tx) => {
// @ts-expect-error
await listener(args, { db: tx, logger: childLogger });
});
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion src/event-handlers/ready.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { createSurveyCommand } from "../interactions/commands/create-survey/comm
import { editSurveyCommand } from "../interactions/commands/edit-survey/command";
import { tagPendingCommand } from "../interactions/commands/tag-pending/command";

const commands: RESTPutAPIApplicationCommandsJSONBody | RESTPutAPIApplicationGuildCommandsJSONBody =
const commands: RESTPutAPIApplicationCommandsJSONBody & RESTPutAPIApplicationGuildCommandsJSONBody =
[createSurveyCommand, editSurveyCommand, tagPendingCommand];

export const handleReady = createEventHandler(
Expand Down
2 changes: 1 addition & 1 deletion src/global-state/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const getMembersPage = (
api: API,
guildSnowflake: Snowflake,
previousPage?: RESTGetAPIGuildMembersResult,
) =>
): Promise<RESTGetAPIGuildMembersResult> =>
// PERMISSIONS: Server Members Intent (Privileged Gateway Intents)
api.guilds.getMembers(guildSnowflake, {
limit: maxLimit,
Expand Down
6 changes: 2 additions & 4 deletions src/interactions/commands/create-survey/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import type {
import { ApplicationCommandOptionType, ApplicationCommandType, ChannelType } from "@discordjs/core";
import { createSurveyCommandMessages } from "../../../messages";

export const createSurveyCommand: (
| RESTPutAPIApplicationCommandsJSONBody
| RESTPutAPIApplicationGuildCommandsJSONBody
)[number] = {
export const createSurveyCommand: (RESTPutAPIApplicationCommandsJSONBody &
RESTPutAPIApplicationGuildCommandsJSONBody)[number] = {
type: ApplicationCommandType.ChatInput,
name: createSurveyCommandMessages.commandName,
description: createSurveyCommandMessages.commandDescription,
Expand Down
4 changes: 2 additions & 2 deletions src/interactions/commands/create-survey/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { Db } from "../../../db";
import { getGroups } from "../../../global-state/groups";
import { logger } from "../../../logger";
import { channelUrl, createSurveyCommandMessages } from "../../../messages";
import { embedFromMembers } from "../../../utils/embed";
import { embedFromGroups } from "../../../utils/embed";
import { Status } from "../../../utils/embed/status";
import { exponentialBackoff } from "../../../utils/exponential-backoff";
import { InteractionError } from "../../error";
Expand Down Expand Up @@ -49,7 +49,7 @@ export async function handleCreateSurveyCommand(
threadSnowflake !== undefined ? channelUrl(data.guild_id, threadSnowflake) : undefined;
await api.interactions.reply(data.id, data.token, {
embeds: [
embedFromMembers(await getGroups(db, data.guild_id), { title: embedTitle, url: threadUrl }),
embedFromGroups(await getGroups(db, data.guild_id), { title: embedTitle, url: threadUrl }),
],
components,
});
Expand Down
6 changes: 2 additions & 4 deletions src/interactions/commands/edit-survey/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import type {
import { ApplicationCommandType } from "@discordjs/core";
import { editSurveyCommandMessages } from "../../../messages";

export const editSurveyCommand: (
| RESTPutAPIApplicationCommandsJSONBody
| RESTPutAPIApplicationGuildCommandsJSONBody
)[number] = {
export const editSurveyCommand: (RESTPutAPIApplicationCommandsJSONBody &
RESTPutAPIApplicationGuildCommandsJSONBody)[number] = {
type: ApplicationCommandType.Message,
name: editSurveyCommandMessages.commandName,
};
6 changes: 5 additions & 1 deletion src/interactions/commands/is-admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { guilds } from "../../db/schema/guilds";
import { members } from "../../db/schema/members";
import { users } from "../../db/schema/users";

export async function isAdmin(db: Db, guildSnowflake: Snowflake, userSnowflake: Snowflake) {
export async function isAdmin(
db: Db,
guildSnowflake: Snowflake,
userSnowflake: Snowflake,
): Promise<boolean> {
const guild = await db.query.guilds.findFirst({
columns: { id: true },
where: eq(guilds.snowflake, guildSnowflake),
Expand Down
6 changes: 2 additions & 4 deletions src/interactions/commands/tag-pending/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import type {
import { ApplicationCommandType } from "@discordjs/core";
import { tagPendingCommandMessages } from "../../../messages";

export const tagPendingCommand: (
| RESTPutAPIApplicationCommandsJSONBody
| RESTPutAPIApplicationGuildCommandsJSONBody
)[number] = {
export const tagPendingCommand: (RESTPutAPIApplicationCommandsJSONBody &
RESTPutAPIApplicationGuildCommandsJSONBody)[number] = {
type: ApplicationCommandType.Message,
name: tagPendingCommandMessages.commandName,
};
7 changes: 4 additions & 3 deletions src/interactions/components/edit-survey/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { ChannelType } from "@discordjs/core";
import type { Db } from "../../../db";
import { logger } from "../../../logger";
import { editSurveyComponentInteractionMessages, parseChannelUrl } from "../../../messages";
import { embedFromMembers, membersFromEmbed } from "../../../utils/embed";
import { embedFromGroups, membersFromEmbed } from "../../../utils/embed";
import { InteractionError } from "../../error";
import type { EditSurveyComponentInteractionData } from "./data";

const notEmptyOrUndefined = (string: string) => (string !== "" ? string : undefined);
const notEmptyOrUndefined = (string: string): string | undefined =>
string !== "" ? string : undefined;

export async function handleEditSurveyComponentInteraction(
api: API,
Expand Down Expand Up @@ -48,6 +49,6 @@ export async function handleEditSurveyComponentInteraction(
await api.interactions.deferMessageUpdate(data.id, data.token);
// PERMISSIONS: Embed Links
await api.channels.editMessage(surveyMessage.channel_id, surveyMessage.id, {
embeds: [embedFromMembers(members, { title, url, informations })],
embeds: [embedFromGroups(members, { title, url, informations })],
});
}
4 changes: 2 additions & 2 deletions src/interactions/components/survey-button/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { MessageFlags } from "@discordjs/core";
import type { Db } from "../../../db";
import { logger } from "../../../logger";
import { parseChannelUrl, surveyComponentInteractionMessages } from "../../../messages";
import { embedFromMembers, informationsFromEmbed, membersFromEmbed } from "../../../utils/embed";
import { embedFromGroups, informationsFromEmbed, membersFromEmbed } from "../../../utils/embed";
import { Status } from "../../../utils/embed/status";
import { tagFromSnowflake } from "../../../utils/embed/tag";
import type { SurveyButtonComponentInteractionData } from "./data";
Expand Down Expand Up @@ -52,7 +52,7 @@ export async function handleSurveyComponentInteraction(

await api.interactions.updateMessage(data.id, data.token, {
embeds: [
embedFromMembers(members, {
embedFromGroups(members, {
title: data.message.embeds[0].title,
url: data.message.embeds[0].url,
informations: informationsFromEmbed(data.message.embeds[0]),
Expand Down
2 changes: 1 addition & 1 deletion src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const logger = pino({
},
useOnlyCustomLevels: true,
formatters: {
level(label) {
level(label): { level: string } {
return { level: label };
},
},
Expand Down
4 changes: 2 additions & 2 deletions src/utils/embed/build-group-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type { Groups } from "../../global-state/groups";
import type { Status } from "./status";
import { tagFromSnowflake } from "./tag";

export const buildGroupFields = (members: Groups): APIEmbedField[] =>
members.map(({ name: groupName, members: groupMembers }) => ({
export const buildGroupFields = (groups: Groups): APIEmbedField[] =>
groups.map(({ name: groupName, members: groupMembers }) => ({
name: groupName,
value: groupMembers
.filter(
Expand Down
4 changes: 2 additions & 2 deletions src/utils/embed/build-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import type { APIEmbedFooter } from "@discordjs/core";
import type { Groups } from "../../global-state/groups";
import { Status } from "./status";

export function buildSummary(members: Groups): APIEmbedFooter {
export function buildSummary(groups: Groups): APIEmbedFooter {
const totals = {
[Status.Ok]: new Set(),
[Status.Perhaps]: new Set(),
[Status.No]: new Set(),
};
for (const { members: groupMembers } of members) {
for (const { members: groupMembers } of groups) {
for (const { snowflake, status } of groupMembers) {
if (status !== undefined) {
totals[status].add(snowflake);
Expand Down
Loading

0 comments on commit e08b335

Please sign in to comment.