feat: add a database client
Some checks failed
Node.js CI / Lint and Test (pull_request) Failing after 11s

This commit is contained in:
2025-07-02 10:52:11 -04:00
parent a632f2b924
commit 7e9dc20632
8 changed files with 174 additions and 6 deletions

View File

@ -26,6 +26,7 @@
"dependencies": {
"@nhcarrigan/logger": "1.0.0",
"discord.js": "14.21.0",
"mongodb": "6.17.0",
"node-schedule": "2.1.1"
}
}

104
pnpm-lock.yaml generated
View File

@ -14,6 +14,9 @@ importers:
discord.js:
specifier: 14.21.0
version: 14.21.0
mongodb:
specifier: ^6.17.0
version: 6.17.0
node-schedule:
specifier: 2.1.1
version: 2.1.1
@ -405,6 +408,9 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@mongodb-js/saslprep@1.3.0':
resolution: {integrity: sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==}
'@nhcarrigan/eslint-config@5.2.0':
resolution: {integrity: sha512-YpTTqhviKMlRwKF+RC/GYiA5i2jTCmg8uftuiufldneNV5HMbGpTfBbV7tpa8++5mpYJc4+eZaf40QbDiz84dQ==}
engines: {node: '>=22', pnpm: '>=9'}
@ -584,6 +590,12 @@ packages:
'@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
'@types/webidl-conversions@7.0.3':
resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==}
'@types/whatwg-url@11.0.5':
resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==}
'@types/ws@8.5.14':
resolution: {integrity: sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==}
@ -849,6 +861,10 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
bson@6.10.4:
resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==}
engines: {node: '>=16.20.1'}
builtin-modules@3.3.0:
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
engines: {node: '>=6'}
@ -1641,6 +1657,9 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
memory-pager@1.5.0:
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@ -1667,6 +1686,36 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
mongodb-connection-string-url@3.0.2:
resolution: {integrity: sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==}
mongodb@6.17.0:
resolution: {integrity: sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==}
engines: {node: '>=16.20.1'}
peerDependencies:
'@aws-sdk/credential-providers': ^3.188.0
'@mongodb-js/zstd': ^1.1.0 || ^2.0.0
gcp-metadata: ^5.2.0
kerberos: ^2.0.1
mongodb-client-encryption: '>=6.0.0 <7'
snappy: ^7.2.2
socks: ^2.7.1
peerDependenciesMeta:
'@aws-sdk/credential-providers':
optional: true
'@mongodb-js/zstd':
optional: true
gcp-metadata:
optional: true
kerberos:
optional: true
mongodb-client-encryption:
optional: true
snappy:
optional: true
socks:
optional: true
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@ -1978,6 +2027,9 @@ packages:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
sparse-bitfield@3.0.3:
resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==}
spdx-correct@3.2.0:
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
@ -2091,6 +2143,10 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
tr46@5.1.1:
resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
engines: {node: '>=18'}
ts-api-utils@1.4.3:
resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==}
engines: {node: '>=16'}
@ -2245,6 +2301,14 @@ packages:
jsdom:
optional: true
webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
whatwg-url@14.2.0:
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
engines: {node: '>=18'}
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
@ -2650,6 +2714,10 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@mongodb-js/saslprep@1.3.0':
dependencies:
sparse-bitfield: 3.0.3
'@nhcarrigan/eslint-config@5.2.0(@typescript-eslint/utils@8.25.0(eslint@9.30.0)(typescript@5.8.3))(eslint@9.30.0)(playwright@1.50.1)(react@19.0.0)(typescript@5.8.3)(vitest@3.2.4(@types/node@24.0.6))':
dependencies:
'@eslint-community/eslint-plugin-eslint-comments': 4.4.1(eslint@9.30.0)
@ -2804,6 +2872,12 @@ snapshots:
'@types/normalize-package-data@2.4.4': {}
'@types/webidl-conversions@7.0.3': {}
'@types/whatwg-url@11.0.5':
dependencies:
'@types/webidl-conversions': 7.0.3
'@types/ws@8.5.14':
dependencies:
'@types/node': 24.0.6
@ -3163,6 +3237,8 @@ snapshots:
node-releases: 2.0.19
update-browserslist-db: 1.1.2(browserslist@4.24.4)
bson@6.10.4: {}
builtin-modules@3.3.0: {}
cac@6.7.14: {}
@ -4129,6 +4205,8 @@ snapshots:
math-intrinsics@1.1.0: {}
memory-pager@1.5.0: {}
merge2@1.4.1: {}
micromatch@4.0.8:
@ -4150,6 +4228,17 @@ snapshots:
minipass@7.1.2: {}
mongodb-connection-string-url@3.0.2:
dependencies:
'@types/whatwg-url': 11.0.5
whatwg-url: 14.2.0
mongodb@6.17.0:
dependencies:
'@mongodb-js/saslprep': 1.3.0
bson: 6.10.4
mongodb-connection-string-url: 3.0.2
ms@2.1.3: {}
nanoid@3.3.8: {}
@ -4499,6 +4588,10 @@ snapshots:
source-map-js@1.2.1: {}
sparse-bitfield@3.0.3:
dependencies:
memory-pager: 1.5.0
spdx-correct@3.2.0:
dependencies:
spdx-expression-parse: 3.0.1
@ -4634,6 +4727,10 @@ snapshots:
dependencies:
is-number: 7.0.0
tr46@5.1.1:
dependencies:
punycode: 2.3.1
ts-api-utils@1.4.3(typescript@5.8.3):
dependencies:
typescript: 5.8.3
@ -4795,6 +4892,13 @@ snapshots:
- tsx
- yaml
webidl-conversions@7.0.0: {}
whatwg-url@14.2.0:
dependencies:
tr46: 5.1.1
webidl-conversions: 7.0.0
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0

View File

@ -1,2 +1,3 @@
DISCORD_TOKEN="op://Environment Variables - Naomi/Standup Bot/discord_token"
LOG_TOKEN="op://Environment Variables - Naomi/Alert Server/api_auth"
MONGO_DB_URL="op://Environment Variables - Naomi/Database/mongo_url"

View File

@ -4,14 +4,12 @@
* @author Naomi Carrigan
*/
import type { ReminderChannel } from "../models/channel";
/**
* The channels to post standup reminders in.
*/
export const channels: Array<{
channelId: string;
roleId: string;
name: string;
}> = [
export const channels: Array<ReminderChannel> = [
{
channelId: "1382093555228606484",
name: "red-script",

View File

@ -7,6 +7,7 @@ import { Client, Events, GatewayIntentBits } from "discord.js";
import { scheduleJob } from "node-schedule";
import { channels } from "./config/channels.js";
import { standup } from "./modules/standup.js";
import { closeDatabase, initializeDatabase } from "./utils/database.js";
import { logger } from "./utils/logger.js";
const activeIds: Array<string> = [];
@ -17,6 +18,7 @@ process.on("unhandledRejection", (error) => {
return;
}
void logger.error("unhandled rejection", new Error(String(error)));
void closeDatabase();
});
process.on("uncaughtException", (error) => {
@ -25,12 +27,15 @@ process.on("uncaughtException", (error) => {
return;
}
void logger.error("uncaught exception", new Error(String(error)));
void closeDatabase();
});
const client = new Client({
intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages ],
});
await initializeDatabase();
client.on(Events.ClientReady, () => {
void logger.log("debug", "Bot is ready.");
scheduleJob("reminders", "0 9 * * 1-5", async() => {

11
src/models/channel.ts Normal file
View File

@ -0,0 +1,11 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Anna
*/
export interface ReminderChannel {
channelId: string;
roleId: string;
name: string;
}

47
src/utils/database.ts Normal file
View File

@ -0,0 +1,47 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Anna
*/
import { type Collection, MongoClient } from "mongodb";
import type { ReminderChannel } from "../models/channel";
const client = new MongoClient(process.env.MONGO_DB_URL ?? "");
const databaseName = "Maribelle";
// eslint-disable-next-line @typescript-eslint/init-declarations -- I need a reference to the collection to insert records
let collection: Collection<ReminderChannel>;
/**
* Initializes a connection to the database
* using the environment variable.
*/
async function initializeDatabase(): Promise<void> {
await client.connect();
const database = client.db(databaseName);
collection = database.collection<ReminderChannel>("channel");
}
/**
* Inserts a new channel into the database.
* @param channelId - The id of the channel to post in.
* @param roleId - The id of the role to ping.
* @param name - The name of the group.
*/
async function insertRecord(channelId: string,
roleId: string,
name: string): Promise<void> {
const document = { channelId, name, roleId };
await collection.insertOne(document);
}
/**
* Closes the connection to the database.
*/
async function closeDatabase(): Promise<void> {
await client.close();
}
export { closeDatabase, initializeDatabase, insertRecord };

View File

@ -10,3 +10,4 @@ export const logger = new Logger(
"Maribelle",
process.env.LOG_TOKEN ?? "",
);