Skip to content

Commit

Permalink
Merge pull request #128 from TogetherCrew/development
Browse files Browse the repository at this point in the history
Merge Development into the Main
  • Loading branch information
cyri113 authored Oct 23, 2023
2 parents f6e5d9e + 3042bb3 commit 0f7d921
Show file tree
Hide file tree
Showing 35 changed files with 2,588 additions and 1,649 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# env vars
src/config/*.env
*.env

# compiled output
/lib
/dist
/node_modules

#migration
migrate.json
.migrate
# Logs
logs
*.log
Expand Down
2,940 changes: 1,762 additions & 1,178 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
"test:ci": "jest --ci --detectOpenHandles",
"lint": "eslint **/*.ts",
"lint-fix": "eslint --fix **/*.ts",
"format": "prettier --write src/**/*.ts"
"format": "prettier --write src/**/*.ts",
"migrate:create": "migrate create --template-file ./src/migrations/utils/template.ts --migrations-dir=\"./src/migrations/db\"",
"migrate:up": "migrate --migrations-dir=\"./lib/migrations/db\" up",
"migrate:down": "migrate --migrations-dir=\"./lib/migrations/db\" down"
},
"repository": {
"type": "git",
Expand All @@ -26,16 +29,18 @@
"homepage": "https://github.com/Behzad-rabiei/tc-discordBot#readme",
"dependencies": {
"@sentry/node": "^7.51.2",
"@togethercrew.dev/db": "^2.4.95",
"@togethercrew.dev/db": "^2.5.1",
"@togethercrew.dev/tc-messagebroker": "^0.0.40",
"babel-jest": "^29.5.0",
"bullmq": "^3.14.0",
"discord.js": "^14.12.1",
"joi": "^17.9.2",
"migrate": "^2.0.0",
"moment": "^2.29.4",
"mongodb": "^5.4.0",
"mongoose": "^6.11.1",
"node-fetch": "^2.6.7",
"pino": "^8.15.0",
"redis": "^4.6.6"
},
"devDependencies": {
Expand All @@ -62,4 +67,4 @@
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
}
}
}
4 changes: 4 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const envVarsSchema = Joi.object()
REDIS_HOST: Joi.string().required().description('Reids host'),
REDIS_PORT: Joi.string().required().description('Reids port'),
REDIS_PASSWORD: Joi.string().required().description('Reids password').allow(''),
LOG_LEVEL: Joi.string().required().description('Min allowed log level'),
})
.unknown();

Expand Down Expand Up @@ -51,4 +52,7 @@ export default {
dsn: envVars.SENTRY_DSN,
env: envVars.SENTRY_ENV,
},
logger: {
level: envVars.LOG_LEVEL,
},
};
18 changes: 18 additions & 0 deletions src/config/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pino, { Bindings } from 'pino';
import config from './index';

export default pino({
level: config.logger.level,
formatters: {
level: label => {
return { level: label.toUpperCase() };
},
},
timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`,
bindings: (bindings: Bindings) => {
return {
pid: bindings.pid,
host: bindings.hostname,
};
},
});
10 changes: 7 additions & 3 deletions src/database/connection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Connection } from 'mongoose';
import parentLogger from '../config/logger';

const logger = parentLogger.child({ module: 'Connection' });

/**
* Closes a given Mongoose connection.
* @param {Connection} connection - The Mongoose connection object to be closed.
Expand All @@ -8,8 +12,8 @@ import { Connection } from 'mongoose';
export async function closeConnection(connection: Connection) {
try {
await connection.close();
console.log('The connection to the database has been successfully closed.');
} catch (err) {
console.log('Error closing connection to the database:', err);
logger.info({ database: connection.name }, 'The connection to database has been successfully closed');
} catch (error) {
logger.fatal({ database: connection.name, error }, 'Failed to close the connection to the database');
}
}
11 changes: 7 additions & 4 deletions src/database/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import mongoose from 'mongoose';
import config from '../config';
import logger from '../config/logger';

// Connect to MongoDB
export async function connectDB() {
mongoose.set('strictQuery', false);
mongoose.connect(config.mongoose.serverURL).then(() => {
console.log('Connected to MongoDB!');
});
mongoose
.connect(config.mongoose.serverURL)
.then(() => {
logger.info({ url: config.mongoose.serverURL }, 'Connected to MongoDB!');
})
.catch(error => logger.error({ url: config.mongoose.serverURL, error }, 'Failed to connect to MongoDB!'));
}
95 changes: 71 additions & 24 deletions src/database/services/channel.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Connection } from 'mongoose';
import { IChannel, IChannelMethods, IChannelUpdateBody } from '@togethercrew.dev/db';
import { VoiceChannel, TextChannel, CategoryChannel } from 'discord.js';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ module: 'ChannelService' });

/**
* Create a channel in the database.
Expand All @@ -10,8 +14,13 @@ import { IChannel, IChannelMethods, IChannelUpdateBody } from '@togethercrew.dev
async function createChannel(connection: Connection, channel: IChannel): Promise<IChannel | null> {
try {
return await connection.models.Channel.create(channel);
} catch (error) {
console.log('Failed to create channel', error);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
if (error.code == 11000) {
logger.warn({ database: connection.name, channel_id: channel.channelId }, 'Failed to create duplicate channel');
return null;
}
logger.error({ database: connection.name, channel_id: channel.channelId, error }, 'Failed to create channel');
return null;
}
}
Expand All @@ -25,8 +34,13 @@ async function createChannel(connection: Connection, channel: IChannel): Promise
async function createChannels(connection: Connection, channels: IChannel[]): Promise<IChannel[] | []> {
try {
return await connection.models.Channel.insertMany(channels, { ordered: false });
} catch (error) {
console.log('Failed to create channels', error);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
if (error.code == 11000) {
logger.warn({ database: connection.name }, 'Failed to create duplicate channels');
return [];
}
logger.error({ database: connection.name, error }, 'Failed to create channels');
return [];
}
}
Expand All @@ -38,12 +52,7 @@ async function createChannels(connection: Connection, channels: IChannel[]): Pro
* @returns {Promise<IChannel | null>} - A promise that resolves to the matching channel object or null if not found.
*/
async function getChannel(connection: Connection, filter: object): Promise<(IChannel & IChannelMethods) | null> {
try {
return await connection.models.Channel.findOne(filter);
} catch (error) {
console.log('Failed to retrieve channel', error);
return null;
}
return await connection.models.Channel.findOne(filter);
}

/**
Expand All @@ -53,35 +62,30 @@ async function getChannel(connection: Connection, filter: object): Promise<(ICha
* @returns {Promise<IChannel[] | []>} - A promise that resolves to an array of the matching channel objects.
*/
async function getChannels(connection: Connection, filter: object): Promise<IChannel[] | []> {
try {
return await connection.models.Channel.find(filter);
} catch (error) {
console.log('Failed to retrieve channels', error);
return [];
}
return await connection.models.Channel.find(filter);
}

/**
* Update a channel in the database based on the filter criteria.
* @param {Connection} connection - Mongoose connection object for the database.
* @param {object} filter - An object specifying the filter criteria to match the desired channel entry.
* @param {IChannelUpdateBody} UpdateBody - An object containing the updated channel data.
* @param {IChannelUpdateBody} updateBody - An object containing the updated channel data.
* @returns {Promise<IChannel | null>} - A promise that resolves to the updated channel object or null if not found.
*/
async function updateChannel(
connection: Connection,
filter: object,
UpdateBody: IChannelUpdateBody
updateBody: IChannelUpdateBody
): Promise<IChannel | null> {
try {
const channel = await connection.models.Channel.findOne(filter);
if (!channel) {
return null;
}
Object.assign(channel, UpdateBody);
Object.assign(channel, updateBody);
return await channel.save();
} catch (error) {
console.log('Failed to update channel', error);
logger.error({ database: connection.name, filter, updateBody, error }, 'Failed to update channel');
return null;
}
}
Expand All @@ -90,24 +94,67 @@ async function updateChannel(
* Update multiple channels in the database based on the filter criteria.
* @param {Connection} connection - Mongoose connection object for the database.
* @param {object} filter - An object specifying the filter criteria to match multiple channel entries.
* @param {IChannelUpdateBody} UpdateBody - An object containing the updated channel data.
* @param {IChannelUpdateBody} updateBody - An object containing the updated channel data.
* @returns {Promise<number>} - A promise that resolves to the number of updated channel entries.
*/
async function updateChannels(connection: Connection, filter: object, UpdateBody: IChannelUpdateBody): Promise<number> {
async function updateChannels(connection: Connection, filter: object, updateBody: IChannelUpdateBody): Promise<number> {
try {
const updateResult = await connection.models.Channel.updateMany(filter, UpdateBody);
const updateResult = await connection.models.Channel.updateMany(filter, updateBody);
return updateResult.modifiedCount || 0;
} catch (error) {
console.log('Failed to update channels', error);
logger.error({ database: connection.name, filter, updateBody, error }, 'Failed to update channels');
return 0;
}
}

/**
* Handle the logic for creating or updating channels in the database.
* @param {Connection} connection - Mongoose connection object for the specific guild database.
* @param {TextChannel | VoiceChannel | CategoryChannel} channel - The Discord.js Channel object containing the full channel details.
* @returns {Promise<void>} - A promise that resolves when the create or update operation is complete.
*
*/
async function handelChannelChanges(
connection: Connection,
channel: TextChannel | VoiceChannel | CategoryChannel
): Promise<void> {
const commonFields = getNeededDateFromChannel(channel);
try {
const channelDoc = await updateChannel(connection, { channelId: channel.id }, commonFields);
if (!channelDoc) {
await createChannel(connection, commonFields);
}
} catch (error) {
logger.error({ guild_id: connection.name, channel_id: channel.id, error }, 'Failed to handle channel changes');
}
}

/**
* Extracts necessary fields from a Discord.js GuildMember object to form an IGuildMember object.
* @param {TextChannel | VoiceChannel | CategoryChannel} channel - The Discord.js Channel object containing the full channel details.
* @returns {IChannel} - The extracted data in the form of an IChannel object.
*/
function getNeededDateFromChannel(channel: TextChannel | VoiceChannel | CategoryChannel): IChannel {
return {
channelId: channel.id,
name: channel.name, // cast to TextChannel for 'name'
parentId: channel.parentId,
permissionOverwrites: Array.from(channel.permissionOverwrites.cache.values()).map(overwrite => ({
id: overwrite.id,
type: overwrite.type,
allow: overwrite.allow.bitfield.toString(),
deny: overwrite.deny.bitfield.toString(),
})),
};
}

export default {
createChannel,
createChannels,
updateChannel,
getChannel,
getChannels,
updateChannels,
handelChannelChanges,
getNeededDateFromChannel,
};
36 changes: 22 additions & 14 deletions src/database/services/guild.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Guild, IGuild, IGuildUpdateBody } from '@togethercrew.dev/db';
import { Snowflake, Client } from 'discord.js';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ module: 'GuildService' });

/**
* get guild by query
Expand All @@ -15,54 +19,58 @@ async function getGuild(filter: object): Promise<IGuild | null> {
* @returns {Promise<object[]>} - A promise that resolves to an array of matching guild entries.
*/
async function getGuilds(filter: object): Promise<IGuild[]> {
try {
return await Guild.find(filter);
} catch (error) {
console.log('Failed to retrieve guilds', error);
return [];
}
return await Guild.find(filter);
}

/**
* Update the guild entry that matches the provided filter with the provided data.
* @param {object} filter - Filter criteria to match the desired guild entry for update.
* @param {IGuildUpdateBody} UpdateBody - Updated information for the guild entry.
* @param {IGuildUpdateBody} updateBody - Updated information for the guild entry.
* @returns {Promise<object|null>} - A promise that resolves to the updated guild entry, or null if not found.
*/
async function updateGuild(filter: object, UpdateBody: IGuildUpdateBody): Promise<IGuild | null> {
async function updateGuild(filter: object, updateBody: IGuildUpdateBody): Promise<IGuild | null> {
try {
const guild = await Guild.findOne(filter);
if (!guild) {
return null;
}
Object.assign(guild, UpdateBody);
Object.assign(guild, updateBody);
return await guild.save();
} catch (error) {
console.log('Failed to update guild', error);
logger.error({ database: 'RnDAO', filter, updateBody, error }, 'Failed to update guild');
return null;
}
}

/**
* Update multiple guild entries that match the provided filter with the provided data.
* @param {object} filter - Filter criteria to match the desired guild entries for update.
* @param {IGuildUpdateBody} UpdateBody - Updated information for the guild entry.
* @param {IGuildUpdateBody} updateBody - Updated information for the guild entry.
* @returns {Promise<number>} - A promise that resolves to the number of guild entries updated.
*/
async function updateManyGuilds(filter: object, UpdateBody: IGuildUpdateBody): Promise<number> {
async function updateManyGuilds(filter: object, updateBody: IGuildUpdateBody): Promise<number> {
try {
const updateResult = await Guild.updateMany(filter, UpdateBody);
const updateResult = await Guild.updateMany(filter, updateBody);
const modifiedCount = updateResult.modifiedCount;
return modifiedCount;
} catch (error) {
console.log('Failed to update guilds', error);
logger.error({ database: 'RnDAO', filter, updateBody, error }, 'Failed to update guilds');
return 0;
}
}

async function checkBotAccessToGuild(client: Client, guildId: Snowflake) {
if (!client.guilds.cache.has(guildId)) {
await updateGuild({ guildId }, { isDisconnected: false });
return false;
}
return true;
}

export default {
getGuild,
getGuilds,
updateGuild,
updateManyGuilds,
checkBotAccessToGuild,
};
Loading

0 comments on commit 0f7d921

Please sign in to comment.