diff --git a/.eslintrc.js b/.eslintrc.js index e804067b..06dc5291 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,22 +1,15 @@ module.exports = { - "env": { - "es2021": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "overrides": [ - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - } -} \ No newline at end of file + env: { + es2021: true, + node: true + }, + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + overrides: [], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module' + }, + plugins: ['@typescript-eslint'], + rules: {} +} diff --git a/.eslintrc.json b/.eslintrc.json index 92fa887e..1e46b38a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,24 +1,16 @@ { - "env": { - "ES2017": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "prettier", - "prettier/@typescript-eslint" - ], - "plugins": [ - "@typescript-eslint", - "prettier" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "rules": { - "prettier/prettier": "error" - } -} \ No newline at end of file + "env": { + "ES2017": true, + "node": true + }, + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier", "prettier/@typescript-eslint"], + "plugins": ["@typescript-eslint", "prettier"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "rules": { + "prettier/prettier": "error" + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17715391..842e3bda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,41 +1,9 @@ -name: tc-discordBot +name: Staging CI/CD Pipeline -on: - push: - pull_request: +on: pull_request jobs: - test: - strategy: - matrix: - platform: [ubuntu-latest] - node: ['18.x'] - name: test/node ${{ matrix.node }}/${{ matrix.platform }} - runs-on: ${{ matrix.platform }} - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: ${{ matrix.node }} - - run: npm install - # - run: npm run test:ci - - run: npm run lint - - run: npm run format - - run: npm run build - - # coverage: - # needs: [test] - # name: coverage - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 - # - uses: actions/setup-node@v2 - # with: - # node-version: '18' - # - run: npm install - # - run: npm run build - # - uses: paambaati/codeclimate-action@v3.2.0 - # env: - # CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} - # with: - # coverageCommand: npm run coverage + ci: + uses: TogetherCrew/operations/.github/workflows/ci.yml@main + secrets: + CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} \ No newline at end of file diff --git a/README.md b/README.md index 327ac98a..5ea84ddb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ +# tc-discordBot + [![Maintainability](https://api.codeclimate.com/v1/badges/e1239b895f0ee2569b61/maintainability)](https://codeclimate.com/github/RnDAO/tc-discordBot/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/e1239b895f0ee2569b61/test_coverage)](https://codeclimate.com/github/RnDAO/tc-discordBot/test_coverage) - -# tc-discordBot \ No newline at end of file diff --git a/__tests__/fixtures/channel.fixture.ts b/__tests__/fixtures/channel.fixture.ts index 1246412a..887cb900 100644 --- a/__tests__/fixtures/channel.fixture.ts +++ b/__tests__/fixtures/channel.fixture.ts @@ -2,38 +2,37 @@ import { IChannel } from '@togethercrew.dev/db'; import { Connection } from 'mongoose'; export const channel1: IChannel = { - channelId: '987654321098765432', - name: 'Channel 1', - parentId: null, - permissionOverwrites: [ - { - id: '1122334455', // example Snowflake ID for the role or member - type: 0, - allow: 'VIEW_CHANNEL', - deny: 'SEND_MESSAGES', - }, - { - id: '9988776655', // another example Snowflake ID for the role or member - type: 1, - allow: 'VIEW_CHANNEL,SEND_MESSAGES', - deny: '', - }, - ], + channelId: '987654321098765432', + name: 'Channel 1', + parentId: null, + permissionOverwrites: [ + { + id: '1122334455', // example Snowflake ID for the role or member + type: 0, + allow: 'VIEW_CHANNEL', + deny: 'SEND_MESSAGES', + }, + { + id: '9988776655', // another example Snowflake ID for the role or member + type: 1, + allow: 'VIEW_CHANNEL,SEND_MESSAGES', + deny: '', + }, + ], }; export const channel2: IChannel = { - channelId: '234567890123456789', - name: 'Channel 2', - parentId: '987654321098765432', - + channelId: '234567890123456789', + name: 'Channel 2', + parentId: '987654321098765432', }; export const channel3: IChannel = { - channelId: '345678901234567890', - name: 'Channel 3', - parentId: '987654321098765432' + channelId: '345678901234567890', + name: 'Channel 3', + parentId: '987654321098765432', }; export const insertChannels = async function (channels: Array, connection: Connection) { - await connection.models.Channel.insertMany(channels.map((channel) => (channel))); -}; \ No newline at end of file + await connection.models.Channel.insertMany(channels.map(channel => channel)); +}; diff --git a/__tests__/fixtures/guid.fixture.ts b/__tests__/fixtures/guid.fixture.ts index dede6ee0..48fff802 100644 --- a/__tests__/fixtures/guid.fixture.ts +++ b/__tests__/fixtures/guid.fixture.ts @@ -58,5 +58,5 @@ export const guild3 = { }; export const insertManyGuilds = async function (guilds: Array) { - await Guild.insertMany(guilds.map((guild) => guild)); + await Guild.insertMany(guilds.map(guild => guild)); }; diff --git a/__tests__/fixtures/guildMember.fixture.ts b/__tests__/fixtures/guildMember.fixture.ts index 0ec93a58..9e4b5840 100644 --- a/__tests__/fixtures/guildMember.fixture.ts +++ b/__tests__/fixtures/guildMember.fixture.ts @@ -1,34 +1,34 @@ import { IGuildMember } from '@togethercrew.dev/db'; export const guildMember1: IGuildMember = { - discordId: '123456789', - username: 'JohnDoe', - roles: ['role1Id', 'role2Id'], - joinedAt: new Date('2023-05-01'), - discriminator: '1', - isBot: false, - avatar: 'a1', - permissions: '137411140513358n', + discordId: '123456789', + username: 'JohnDoe', + roles: ['role1Id', 'role2Id'], + joinedAt: new Date('2023-05-01'), + discriminator: '1', + isBot: false, + avatar: 'a1', + permissions: '137411140513358n', }; export const guildMember2: IGuildMember = { - discordId: '987654321', - username: 'JaneSmith', - roles: ['role1Id', 'role2Id'], - joinedAt: new Date('2023-05-01'), - discriminator: '2', - isBot: true, - avatar: 'a2', - permissions: '137411140513357n', + discordId: '987654321', + username: 'JaneSmith', + roles: ['role1Id', 'role2Id'], + joinedAt: new Date('2023-05-01'), + discriminator: '2', + isBot: true, + avatar: 'a2', + permissions: '137411140513357n', }; export const guildMember3: IGuildMember = { - discordId: '555555555', - username: 'AliceJohnson', - roles: ['role2Id', 'role5Id'], - joinedAt: new Date('2023-05-03'), - discriminator: '3', - isBot: false, - avatar: 'a3', - permissions: '137411140513356n', -}; \ No newline at end of file + discordId: '555555555', + username: 'AliceJohnson', + roles: ['role2Id', 'role5Id'], + joinedAt: new Date('2023-05-03'), + discriminator: '3', + isBot: false, + avatar: 'a3', + permissions: '137411140513356n', +}; diff --git a/__tests__/fixtures/rawInfo.fixture.ts b/__tests__/fixtures/rawInfo.fixture.ts index 492f4ec4..e25e4e45 100644 --- a/__tests__/fixtures/rawInfo.fixture.ts +++ b/__tests__/fixtures/rawInfo.fixture.ts @@ -13,7 +13,7 @@ export const rawInfo1: IRawInfo = { messageId: 'message123', threadId: 'thread456', threadName: 'thread789', - channelName: 'c1' + channelName: 'c1', }; export const rawInfo2: IRawInfo = { @@ -29,7 +29,7 @@ export const rawInfo2: IRawInfo = { messageId: 'message012', threadId: 'thread345', threadName: 'Discussion Thread', - channelName: 'c2' + channelName: 'c2', }; export const rawInfo3: IRawInfo = { @@ -45,5 +45,5 @@ export const rawInfo3: IRawInfo = { messageId: 'message654', threadId: 'thread321', threadName: 'Important Announcement', - channelName: 'c3' + channelName: 'c3', }; diff --git a/__tests__/fixtures/role.fixture.ts b/__tests__/fixtures/role.fixture.ts index ea0b5bec..ff87a84e 100644 --- a/__tests__/fixtures/role.fixture.ts +++ b/__tests__/fixtures/role.fixture.ts @@ -2,23 +2,23 @@ import { IRole } from '@togethercrew.dev/db'; import { Connection } from 'mongoose'; export const role1: IRole = { - roleId: '234567890123456777', - name: 'Role 1', - color: 123456 + roleId: '234567890123456777', + name: 'Role 1', + color: 123456, }; export const role2: IRole = { - roleId: '234567890123456787', - name: 'Role 2', - color: 654321 + roleId: '234567890123456787', + name: 'Role 2', + color: 654321, }; export const role3: IRole = { - roleId: '234567890123456797', - name: 'Role 3', - color: 654321 + roleId: '234567890123456797', + name: 'Role 3', + color: 654321, }; export const insertRoles = async function (roles: Array, connection: Connection) { - await connection.models.Role.insertMany(roles.map((role) => (role))); -}; \ No newline at end of file + await connection.models.Role.insertMany(roles.map(role => role)); +}; diff --git a/__tests__/unit/database/services/channel.test.ts b/__tests__/unit/database/services/channel.test.ts index e361eff4..d2556e37 100644 --- a/__tests__/unit/database/services/channel.test.ts +++ b/__tests__/unit/database/services/channel.test.ts @@ -1,198 +1,155 @@ import mongoose, { Connection } from 'mongoose'; -import { - IChannel, - channelSchema, - IChannelUpdateBody, -} from '@togethercrew.dev/db'; +import { IChannel, channelSchema, IChannelUpdateBody } from '@togethercrew.dev/db'; import setupTestDB from '../../../utils/setupTestDB'; -import { - channel1, - channel2, - channel3, - insertChannels -} from '../../../fixtures/channel.fixture'; +import { channel1, channel2, channel3, insertChannels } from '../../../fixtures/channel.fixture'; import { channelService } from '../../../../src/database/services'; import config from '../../../../src/config'; setupTestDB(); describe('channel service', () => { - let connection: Connection; + let connection: Connection; - beforeAll(async () => { - connection = await mongoose.createConnection(config.mongoose.serverURL, { - dbName: 'connection', - }); - connection.model('Channel', channelSchema); + beforeAll(async () => { + connection = await mongoose.createConnection(config.mongoose.serverURL, { + dbName: 'connection', }); - - afterAll(async () => { - await connection.close(); + connection.model('Channel', channelSchema); + }); + + afterAll(async () => { + await connection.close(); + }); + + beforeEach(async () => { + await connection.db.dropDatabase(); + }); + + describe('createChannel', () => { + test('should create a channel', async () => { + const result = await channelService.createChannel(connection, channel1); + expect(result).toBeDefined(); + expect(result?.channelId).toEqual(channel1.channelId); + + const channelDoc1 = await channelService.getChannel(connection, { channelId: channel1.channelId }); + + expect(channelDoc1).toBeDefined(); + expect(channelDoc1).toMatchObject({ + channelId: channel1.channelId, + name: channel1.name, + parentId: channel1.parentId, + }); }); - - beforeEach(async () => { - await connection.db.dropDatabase(); + }); + + describe('createChannels', () => { + test('should create channels', async () => { + const result = await channelService.createChannels(connection, [channel1, channel2]); + expect(result).toMatchObject([channel1, channel2]); + + const channelDoc1 = await channelService.getChannel(connection, { channelId: channel1.channelId }); + + const channelDoc2 = await channelService.getChannel(connection, { channelId: channel2.channelId }); + + expect(channelDoc1).toBeDefined(); + expect(channelDoc1).toMatchObject({ + channelId: channel1.channelId, + name: channel1.name, + parentId: channel1.parentId, + }); + expect(channelDoc2).toBeDefined(); + expect(channelDoc2).toMatchObject({ + channelId: channel2.channelId, + name: channel2.name, + parentId: channel2.parentId, + }); }); - - describe('createChannel', () => { - test('should create a channel', async () => { - const result = await channelService.createChannel( - connection, - channel1 - ); - expect(result).toBeDefined(); - expect(result?.channelId).toEqual(channel1.channelId); - - const channelDoc1 = await channelService.getChannel( - connection, - { channelId: channel1.channelId } - ); - - expect(channelDoc1).toBeDefined(); - expect(channelDoc1).toMatchObject({ - channelId: channel1.channelId, - name: channel1.name, - parentId: channel1.parentId, - - }); - }); + }); + + describe('getChannel', () => { + test('should retrieve an existing channel that match the filter criteria', async () => { + await insertChannels([channel1], connection); + const result = await channelService.getChannel(connection, { + channelId: channel1.channelId, + }); + expect(result).toMatchObject(channel1); }); - - describe('createChannels', () => { - test('should create channels', async () => { - const result = await channelService.createChannels(connection, [ - channel1, - channel2, - ]); - expect(result).toMatchObject([channel1, channel2]); - - const channelDoc1 = await channelService.getChannel( - connection, - { channelId: channel1.channelId } - ); - - const channelDoc2 = await channelService.getChannel( - connection, - { channelId: channel2.channelId } - ); - - expect(channelDoc1).toBeDefined(); - expect(channelDoc1).toMatchObject({ - channelId: channel1.channelId, - name: channel1.name, - parentId: channel1.parentId, - }); - expect(channelDoc2).toBeDefined(); - expect(channelDoc2).toMatchObject({ - channelId: channel2.channelId, - name: channel2.name, - parentId: channel2.parentId, - }); - }); + test('should return null when no channel match the filter criteria', async () => { + await insertChannels([channel1], connection); + const result = await channelService.getChannel(connection, { + channelId: channel2.channelId, + }); + expect(result).toBe(null); }); - - describe('getChannel', () => { - test('should retrieve an existing channel that match the filter criteria', async () => { - await insertChannels([channel1], connection); - const result = await channelService.getChannel(connection, { - channelId: channel1.channelId, - }); - expect(result).toMatchObject(channel1); - }); - test('should return null when no channel match the filter criteria', async () => { - await insertChannels([channel1], connection); - const result = await channelService.getChannel(connection, { - channelId: channel2.channelId, - }); - expect(result).toBe(null); - }); + }); + + describe('getChannels', () => { + test('should retrieve channels that match the filter criteria', async () => { + await insertChannels([channel1, channel2, channel3], connection); + const result = await channelService.getChannels(connection, { + parentId: channel2.parentId, + }); + expect(result).toMatchObject([channel2, channel3]); }); - describe('getChannels', () => { - test('should retrieve channels that match the filter criteria', async () => { - await insertChannels([channel1, channel2, channel3], connection); - const result = await channelService.getChannels(connection, { - parentId: channel2.parentId, - }); - expect(result).toMatchObject([channel2, channel3]); - }); - - test('should return an empty array when no channels match the filter criteria', async () => { - await insertChannels([channel1, channel2, channel3], connection); - const result = await channelService.getChannels(connection, { - parentId: "111111", - }); - expect(result).toEqual([]); - }); + test('should return an empty array when no channels match the filter criteria', async () => { + await insertChannels([channel1, channel2, channel3], connection); + const result = await channelService.getChannels(connection, { + parentId: '111111', + }); + expect(result).toEqual([]); }); - - describe('updateChannel', () => { - const updateBody: IChannelUpdateBody = { - name: 'Channel 10', - parentId: "111111" - }; - test('should update an existing channel that match the filter criteria', async () => { - await insertChannels([channel1], connection); - const result = await channelService.updateChannel( - connection, - { channelId: channel1.channelId }, - updateBody - ); - expect(result).toMatchObject(updateBody); - - const channelDoc1 = await channelService.getChannel( - connection, - { channelId: channel1.channelId } - ); - - expect(channelDoc1).toBeDefined(); - expect(channelDoc1).toMatchObject({ - name: updateBody.name, - parentId: updateBody.parentId, - }); - }); - - test('should return null when no channel match the filter criteria', async () => { - const result = await channelService.updateChannel( - connection, - { channelId: channel1.channelId }, - updateBody - ); - expect(result).toEqual(null); - }); + }); + + describe('findOneChannelAndUpdate', () => { + const updateBody: IChannelUpdateBody = { + name: 'Channel 10', + parentId: '111111', + }; + test('should update an existing channel that match the filter criteria', async () => { + await insertChannels([channel1], connection); + const result = await channelService.findOneChannelAndUpdate( + connection, + { channelId: channel1.channelId }, + updateBody + ); + expect(result).toMatchObject(updateBody); + + const channelDoc1 = await channelService.getChannel(connection, { channelId: channel1.channelId }); + + expect(channelDoc1).toBeDefined(); + expect(channelDoc1).toMatchObject({ + name: updateBody.name, + parentId: updateBody.parentId, + }); }); - describe('updateChannels', () => { - const updateBody: IChannelUpdateBody = { - parentId: "111111" - }; - test('should update channels that match the filter criteria', async () => { - await insertChannels([channel1, channel2, channel3], connection); - const result = await channelService.updateChannels( - connection, - { parentId: channel2.parentId }, - updateBody - ); - expect(result).toEqual(2); - const channelDoc1 = await channelService.getChannel( - connection, - { channelId: channel2.channelId } - ); - const channelDoc12 = await channelService.getChannel( - connection, - { channelId: channel3.channelId } - ); - expect(channelDoc1?.parentId).toBe(updateBody.parentId); - expect(channelDoc12?.parentId).toBe(updateBody.parentId); - }); - - test('should return 0 when no channels match the filter criteria', async () => { - const result = await channelService.updateChannels( - connection, - { parentId: channel2.parentId }, - updateBody - ); - expect(result).toEqual(0); - }); + test('should return null when no channel match the filter criteria', async () => { + const result = await channelService.findOneChannelAndUpdate( + connection, + { channelId: channel1.channelId }, + updateBody + ); + expect(result).toEqual(null); + }); + }); + + describe('updateChannels', () => { + const updateBody: IChannelUpdateBody = { + parentId: '111111', + }; + test('should update channels that match the filter criteria', async () => { + await insertChannels([channel1, channel2, channel3], connection); + const result = await channelService.updateChannels(connection, { parentId: channel2.parentId }, updateBody); + expect(result).toEqual(2); + const channelDoc1 = await channelService.getChannel(connection, { channelId: channel2.channelId }); + const channelDoc12 = await channelService.getChannel(connection, { channelId: channel3.channelId }); + expect(channelDoc1?.parentId).toBe(updateBody.parentId); + expect(channelDoc12?.parentId).toBe(updateBody.parentId); }); + test('should return 0 when no channels match the filter criteria', async () => { + const result = await channelService.updateChannels(connection, { parentId: channel2.parentId }, updateBody); + expect(result).toEqual(0); + }); + }); }); diff --git a/__tests__/unit/database/services/guild.test.ts b/__tests__/unit/database/services/guild.test.ts index 44e32d9b..a089daaa 100644 --- a/__tests__/unit/database/services/guild.test.ts +++ b/__tests__/unit/database/services/guild.test.ts @@ -1,10 +1,6 @@ import { Guild, IGuildUpdateBody } from '@togethercrew.dev/db'; import { guildService } from '../../../../src/database/services'; -import { - insertManyGuilds, - guild1, - guild2, -} from '../../../fixtures/guid.fixture'; +import { insertManyGuilds, guild1, guild2 } from '../../../fixtures/guid.fixture'; import setupTestDB from '../../../utils/setupTestDB'; setupTestDB(); @@ -50,10 +46,7 @@ describe('guildService', () => { }; test('should update an existing guild that matches the filter criteria', async () => { await insertManyGuilds([guild1]); - const result = await guildService.updateGuild( - { guildId: guild1.guildId }, - updateBody - ); + const result = await guildService.updateGuild({ guildId: guild1.guildId }, updateBody); expect(result).toMatchObject({ id: guild1?._id.toHexString(), @@ -71,10 +64,7 @@ describe('guildService', () => { }); }); test('should return null when no guild matches the filter criteria', async () => { - const result = await guildService.updateGuild( - { guildId: 'notExistId' }, - updateBody - ); + const result = await guildService.updateGuild({ guildId: 'notExistId' }, updateBody); expect(result).toBeNull(); }); }); @@ -91,18 +81,12 @@ describe('guildService', () => { }; test('should update an existing guilds that matches the filter criteria', async () => { await insertManyGuilds([guild1, guild2]); - const result = await guildService.updateManyGuilds( - { user: guild1.user }, - updateBody - ); + const result = await guildService.updateManyGuilds({ user: guild1.user }, updateBody); expect(result).toBe(2); }); test('should return null when no guild matches the filter criteria', async () => { - const result = await guildService.updateManyGuilds( - { guildId: 'notExistId' }, - updateBody - ); + const result = await guildService.updateManyGuilds({ guildId: 'notExistId' }, updateBody); expect(result).toBe(0); }); diff --git a/__tests__/unit/database/services/guildMember.test.ts b/__tests__/unit/database/services/guildMember.test.ts index 4692ecd8..1e04362a 100644 --- a/__tests__/unit/database/services/guildMember.test.ts +++ b/__tests__/unit/database/services/guildMember.test.ts @@ -1,15 +1,7 @@ import mongoose, { Connection } from 'mongoose'; -import { - IGuildMember, - guildMemberSchema, - IGuildMemberUpdateBody, -} from '@togethercrew.dev/db'; +import { IGuildMember, guildMemberSchema, IGuildMemberUpdateBody } from '@togethercrew.dev/db'; import setupTestDB from '../../../utils/setupTestDB'; -import { - guildMember1, - guildMember2, - guildMember3, -} from '../../../fixtures/guildMember.fixture'; +import { guildMember1, guildMember2, guildMember3 } from '../../../fixtures/guildMember.fixture'; import { guildMemberService } from '../../../../src/database/services'; import config from '../../../../src/config'; setupTestDB(); @@ -34,17 +26,13 @@ describe('guildMember service', () => { describe('createGuidMember', () => { test('should create a guild member', async () => { - const result = await guildMemberService.createGuildMember( - connection, - guildMember1 - ); + const result = await guildMemberService.createGuildMember(connection, guildMember1); expect(result).toBeDefined(); expect(result?.discordId).toEqual(guildMember1.discordId); - const guildMemberDoc1 = await guildMemberService.getGuildMember( - connection, - { discordId: guildMember1.discordId } - ); + const guildMemberDoc1 = await guildMemberService.getGuildMember(connection, { + discordId: guildMember1.discordId, + }); expect(guildMemberDoc1).toBeDefined(); expect(guildMemberDoc1).toMatchObject({ @@ -56,21 +44,16 @@ describe('guildMember service', () => { describe('createGuidMembers', () => { test('should create guild members', async () => { - const result = await guildMemberService.createGuildMembers(connection, [ - guildMember1, - guildMember2, - ]); + const result = await guildMemberService.createGuildMembers(connection, [guildMember1, guildMember2]); expect(result).toMatchObject([guildMember1, guildMember2]); - const guildMemberDoc1 = await guildMemberService.getGuildMember( - connection, - { discordId: guildMember1.discordId } - ); + const guildMemberDoc1 = await guildMemberService.getGuildMember(connection, { + discordId: guildMember1.discordId, + }); - const guildMemberDoc2 = await guildMemberService.getGuildMember( - connection, - { discordId: guildMember2.discordId } - ); + const guildMemberDoc2 = await guildMemberService.getGuildMember(connection, { + discordId: guildMember2.discordId, + }); expect(guildMemberDoc1).toBeDefined(); expect(guildMemberDoc1).toMatchObject({ @@ -104,11 +87,7 @@ describe('guildMember service', () => { describe('getGuildMembers', () => { test('should retrieve guild members that match the filter criteria', async () => { - await guildMemberService.createGuildMembers(connection, [ - guildMember1, - guildMember2, - guildMember3, - ]); + await guildMemberService.createGuildMembers(connection, [guildMember1, guildMember2, guildMember3]); const result = await guildMemberService.getGuildMembers(connection, { roles: guildMember2.roles, }); @@ -116,11 +95,7 @@ describe('guildMember service', () => { }); test('should return an empty array when no guild members match the filter criteria', async () => { - await guildMemberService.createGuildMembers(connection, [ - guildMember1, - guildMember2, - guildMember3, - ]); + await guildMemberService.createGuildMembers(connection, [guildMember1, guildMember2, guildMember3]); const result = await guildMemberService.getGuildMembers(connection, { roles: ['role8'], }); @@ -143,10 +118,9 @@ describe('guildMember service', () => { ); expect(result).toMatchObject(updateBody); - const guildMember1Doc = await guildMemberService.getGuildMember( - connection, - { discordId: guildMember1.discordId } - ); + const guildMember1Doc = await guildMemberService.getGuildMember(connection, { + discordId: guildMember1.discordId, + }); expect(guildMember1Doc).toBeDefined(); expect(guildMember1Doc).toMatchObject({ @@ -171,35 +145,21 @@ describe('guildMember service', () => { avatar: 'new-avatar.png', }; test('should update guild members that match the filter criteria', async () => { - await guildMemberService.createGuildMembers(connection, [ - guildMember1, - guildMember2, - guildMember3, - ]); - const result = await guildMemberService.updateGuildMembers( - connection, - { roles: guildMember2.roles }, - updateBody - ); + await guildMemberService.createGuildMembers(connection, [guildMember1, guildMember2, guildMember3]); + const result = await guildMemberService.updateGuildMembers(connection, { roles: guildMember2.roles }, updateBody); expect(result).toEqual(2); - const guildMember1Doc = await guildMemberService.getGuildMember( - connection, - { discordId: guildMember1.discordId } - ); - const guildMember2Doc = await guildMemberService.getGuildMember( - connection, - { discordId: guildMember2.discordId } - ); + const guildMember1Doc = await guildMemberService.getGuildMember(connection, { + discordId: guildMember1.discordId, + }); + const guildMember2Doc = await guildMemberService.getGuildMember(connection, { + discordId: guildMember2.discordId, + }); expect(guildMember1Doc?.avatar).toBe(updateBody.avatar); expect(guildMember2Doc?.avatar).toBe(updateBody.avatar); }); test('should return 0 when no guild members match the filter criteria', async () => { - const result = await guildMemberService.updateGuildMembers( - connection, - { roles: guildMember2.roles }, - updateBody - ); + const result = await guildMemberService.updateGuildMembers(connection, { roles: guildMember2.roles }, updateBody); expect(result).toEqual(0); }); }); @@ -223,11 +183,7 @@ describe('guildMember service', () => { describe('deleteGuildMembers', () => { test('should delete guild members that match the filter criteria', async () => { - await guildMemberService.createGuildMembers(connection, [ - guildMember1, - guildMember2, - guildMember3, - ]); + await guildMemberService.createGuildMembers(connection, [guildMember1, guildMember2, guildMember3]); const result = await guildMemberService.deleteGuildMembers(connection, { roles: guildMember2.roles, }); @@ -241,4 +197,4 @@ describe('guildMember service', () => { expect(result).toEqual(0); }); }); -}); \ No newline at end of file +}); diff --git a/__tests__/unit/database/services/rawInfo.test.ts b/__tests__/unit/database/services/rawInfo.test.ts index 4c5f0f0e..9b65fd89 100644 --- a/__tests__/unit/database/services/rawInfo.test.ts +++ b/__tests__/unit/database/services/rawInfo.test.ts @@ -1,11 +1,7 @@ import mongoose, { Connection } from 'mongoose'; import { IRawInfo, IRawInfoUpdateBody, rawInfoSchema } from '@togethercrew.dev/db'; import setupTestDB from '../../../utils/setupTestDB'; -import { - rawInfo1, - rawInfo2, - rawInfo3, -} from '../../../fixtures/rawInfo.fixture'; +import { rawInfo1, rawInfo2, rawInfo3 } from '../../../fixtures/rawInfo.fixture'; import { rawInfoService } from '../../../../src/database/services'; import config from '../../../../src/config'; @@ -49,11 +45,7 @@ describe('rawInfo service', () => { describe('createRawInfos', () => { test('should create rawInfos (list of rawInfo)', async () => { - const result = await rawInfoService.createRawInfos(connection, [ - rawInfo1, - rawInfo2, - rawInfo3, - ]); + const result = await rawInfoService.createRawInfos(connection, [rawInfo1, rawInfo2, rawInfo3]); expect(result).toMatchObject([rawInfo1, rawInfo2, rawInfo3]); const rawInfoDoc1 = await rawInfoService.getRawInfo(connection, { @@ -102,11 +94,7 @@ describe('rawInfo service', () => { describe('getRawInfos', () => { test('should retrieve rawInfo that matches the filter criteria', async () => { - await rawInfoService.createRawInfos(connection, [ - rawInfo1, - rawInfo2, - rawInfo3, - ]); + await rawInfoService.createRawInfos(connection, [rawInfo1, rawInfo2, rawInfo3]); const result = await rawInfoService.getRawInfos(connection, { role_mentions: rawInfo2.role_mentions, }); @@ -115,11 +103,7 @@ describe('rawInfo service', () => { }); test('should return an empty array when no rawInfo matches the filter criteria', async () => { - await rawInfoService.createRawInfos(connection, [ - rawInfo1, - rawInfo2, - rawInfo3, - ]); + await rawInfoService.createRawInfos(connection, [rawInfo1, rawInfo2, rawInfo3]); const result = await rawInfoService.getRawInfos(connection, { role_mentions: ['role8'], }); @@ -136,11 +120,7 @@ describe('rawInfo service', () => { test('should update an existing rawInfo that matches the filter criteria', async () => { await rawInfoService.createRawInfo(connection, rawInfo1); - const result = await rawInfoService.updateRawInfo( - connection, - { messageId: rawInfo1.messageId }, - updateBody - ); + const result = await rawInfoService.updateRawInfo(connection, { messageId: rawInfo1.messageId }, updateBody); expect(result).toMatchObject(updateBody); @@ -156,11 +136,7 @@ describe('rawInfo service', () => { }); test('should return null when no rawInfo matches the filter criteria', async () => { - const result = await rawInfoService.updateRawInfo( - connection, - { messageId: rawInfo1.messageId }, - updateBody - ); + const result = await rawInfoService.updateRawInfo(connection, { messageId: rawInfo1.messageId }, updateBody); expect(result).toBeNull(); }); }); @@ -180,14 +156,8 @@ describe('rawInfo service', () => { ); expect(result).toEqual(2); - const rawInfo1Doc = await rawInfoService.getRawInfo( - connection, - updateBody - ); - const rawInfo2Doc = await rawInfoService.getRawInfo( - connection, - updateBody - ); + const rawInfo1Doc = await rawInfoService.getRawInfo(connection, updateBody); + const rawInfo2Doc = await rawInfoService.getRawInfo(connection, updateBody); expect(rawInfo1Doc).toMatchObject({ channelId: updateBody.channelId, @@ -200,11 +170,7 @@ describe('rawInfo service', () => { }); test('should return 0 when no rawInfos match the filter criteria', async () => { - const result = await rawInfoService.updateManyRawInfo( - connection, - { content: rawInfo3.content }, - updateBody - ); + const result = await rawInfoService.updateManyRawInfo(connection, { content: rawInfo3.content }, updateBody); expect(result).toEqual(0); }); }); @@ -228,11 +194,7 @@ describe('rawInfo service', () => { describe('deleteRawInfos', () => { test('should delete rawInfo that matches the filter criteria', async () => { - await rawInfoService.createRawInfos(connection, [ - rawInfo1, - rawInfo2, - rawInfo3, - ]); + await rawInfoService.createRawInfos(connection, [rawInfo1, rawInfo2, rawInfo3]); const result = await rawInfoService.deleteManyRawInfo(connection, { content: rawInfo1.content, }); diff --git a/__tests__/unit/database/services/role.test.ts b/__tests__/unit/database/services/role.test.ts index 421a700a..7fa37dd0 100644 --- a/__tests__/unit/database/services/role.test.ts +++ b/__tests__/unit/database/services/role.test.ts @@ -1,197 +1,147 @@ import mongoose, { Connection } from 'mongoose'; -import { - IRole, - roleSchema, - IRoleUpdateBody, -} from '@togethercrew.dev/db'; +import { IRole, roleSchema, IRoleUpdateBody } from '@togethercrew.dev/db'; import setupTestDB from '../../../utils/setupTestDB'; -import { - role1, - role2, - role3, - insertRoles -} from '../../../fixtures/role.fixture'; +import { role1, role2, role3, insertRoles } from '../../../fixtures/role.fixture'; import { roleService } from '../../../../src/database/services'; import config from '../../../../src/config'; setupTestDB(); describe('role service', () => { - let connection: Connection; + let connection: Connection; - beforeAll(async () => { - connection = await mongoose.createConnection(config.mongoose.serverURL, { - dbName: 'connection', - }); - connection.model('Role', roleSchema); + beforeAll(async () => { + connection = await mongoose.createConnection(config.mongoose.serverURL, { + dbName: 'connection', }); - - afterAll(async () => { - await connection.close(); + connection.model('Role', roleSchema); + }); + + afterAll(async () => { + await connection.close(); + }); + + beforeEach(async () => { + await connection.db.dropDatabase(); + }); + + describe('createRole', () => { + test('should create a role', async () => { + const result = await roleService.createRole(connection, role1); + expect(result).toBeDefined(); + expect(result?.roleId).toEqual(role1.roleId); + + const roleDoc1 = await roleService.getRole(connection, { roleId: role1.roleId }); + + expect(roleDoc1).toBeDefined(); + expect(roleDoc1).toMatchObject({ + roleId: role1.roleId, + name: role1.name, + color: role1.color, + }); }); - - beforeEach(async () => { - await connection.db.dropDatabase(); + }); + + describe('createRoles', () => { + test('should create roles', async () => { + const result = await roleService.createRoles(connection, [role1, role2]); + expect(result).toMatchObject([role1, role2]); + + const roleDoc1 = await roleService.getRole(connection, { roleId: role1.roleId }); + + const roleDoc2 = await roleService.getRole(connection, { roleId: role2.roleId }); + + expect(roleDoc1).toBeDefined(); + expect(roleDoc1).toMatchObject({ + roleId: role1.roleId, + name: role1.name, + color: role1.color, + }); + expect(roleDoc2).toBeDefined(); + expect(roleDoc2).toMatchObject({ + roleId: role2.roleId, + name: role2.name, + color: role2.color, + }); }); - - describe('createRole', () => { - test('should create a role', async () => { - const result = await roleService.createRole( - connection, - role1 - ); - expect(result).toBeDefined(); - expect(result?.roleId).toEqual(role1.roleId); - - const roleDoc1 = await roleService.getRole( - connection, - { roleId: role1.roleId } - ); - - expect(roleDoc1).toBeDefined(); - expect(roleDoc1).toMatchObject({ - roleId: role1.roleId, - name: role1.name, - color: role1.color, - }); - }); + }); + + describe('getRole', () => { + test('should retrieve an existing role that match the filter criteria', async () => { + await insertRoles([role1], connection); + const result = await roleService.getRole(connection, { + roleId: role1.roleId, + }); + expect(result).toMatchObject(role1); }); - - describe('createRoles', () => { - test('should create roles', async () => { - const result = await roleService.createRoles(connection, [ - role1, - role2, - ]); - expect(result).toMatchObject([role1, role2]); - - const roleDoc1 = await roleService.getRole( - connection, - { roleId: role1.roleId } - ); - - const roleDoc2 = await roleService.getRole( - connection, - { roleId: role2.roleId } - ); - - expect(roleDoc1).toBeDefined(); - expect(roleDoc1).toMatchObject({ - roleId: role1.roleId, - name: role1.name, - color: role1.color, - }); - expect(roleDoc2).toBeDefined(); - expect(roleDoc2).toMatchObject({ - roleId: role2.roleId, - name: role2.name, - color: role2.color, - }); - }); + test('should return null when no role match the filter criteria', async () => { + await insertRoles([role1], connection); + const result = await roleService.getRole(connection, { + roleId: role2.roleId, + }); + expect(result).toBe(null); }); - - describe('getRole', () => { - test('should retrieve an existing role that match the filter criteria', async () => { - await insertRoles([role1], connection); - const result = await roleService.getRole(connection, { - roleId: role1.roleId, - }); - expect(result).toMatchObject(role1); - }); - test('should return null when no role match the filter criteria', async () => { - await insertRoles([role1], connection); - const result = await roleService.getRole(connection, { - roleId: role2.roleId, - }); - expect(result).toBe(null); - }); + }); + + describe('getRoles', () => { + test('should retrieve roles that match the filter criteria', async () => { + await insertRoles([role1, role2, role3], connection); + const result = await roleService.getRoles(connection, { + color: role2.color, + }); + expect(result).toMatchObject([role2, role3]); }); - describe('getRoles', () => { - test('should retrieve roles that match the filter criteria', async () => { - await insertRoles([role1, role2, role3], connection); - const result = await roleService.getRoles(connection, { - color: role2.color, - }); - expect(result).toMatchObject([role2, role3]); - }); - - test('should return an empty array when no roles match the filter criteria', async () => { - await insertRoles([role1, role2, role3], connection); - const result = await roleService.getRoles(connection, { - color: 111111, - }); - expect(result).toEqual([]); - }); + test('should return an empty array when no roles match the filter criteria', async () => { + await insertRoles([role1, role2, role3], connection); + const result = await roleService.getRoles(connection, { + color: 111111, + }); + expect(result).toEqual([]); }); - - describe('updateRole', () => { - const updateBody: IRoleUpdateBody = { - name: 'Role 10', - color: 111111 - }; - test('should update an existing role that match the filter criteria', async () => { - await insertRoles([role1], connection); - const result = await roleService.updateRole( - connection, - { roleId: role1.roleId }, - updateBody - ); - expect(result).toMatchObject(updateBody); - - const roleDoc1 = await roleService.getRole( - connection, - { roleId: role1.roleId } - ); - - expect(roleDoc1).toBeDefined(); - expect(roleDoc1).toMatchObject({ - name: updateBody.name, - color: updateBody.color, - }); - }); - - test('should return null when no role match the filter criteria', async () => { - const result = await roleService.updateRole( - connection, - { roleId: role1.roleId }, - updateBody - ); - expect(result).toEqual(null); - }); + }); + + describe('updateRole', () => { + const updateBody: IRoleUpdateBody = { + name: 'Role 10', + color: 111111, + }; + test('should update an existing role that match the filter criteria', async () => { + await insertRoles([role1], connection); + const result = await roleService.updateRole(connection, { roleId: role1.roleId }, updateBody); + expect(result).toMatchObject(updateBody); + + const roleDoc1 = await roleService.getRole(connection, { roleId: role1.roleId }); + + expect(roleDoc1).toBeDefined(); + expect(roleDoc1).toMatchObject({ + name: updateBody.name, + color: updateBody.color, + }); }); - describe('updateRoles', () => { - const updateBody: IRoleUpdateBody = { - color: 111111 - }; - test('should update roles that match the filter criteria', async () => { - await insertRoles([role1, role2, role3], connection); - const result = await roleService.updateRoles( - connection, - { color: role2.color }, - updateBody - ); - expect(result).toEqual(2); - const roleDoc1 = await roleService.getRole( - connection, - { roleId: role2.roleId } - ); - const roleDoc12 = await roleService.getRole( - connection, - { roleId: role3.roleId } - ); - expect(roleDoc1?.color).toBe(updateBody.color); - expect(roleDoc12?.color).toBe(updateBody.color); - }); - - test('should return 0 when no roles match the filter criteria', async () => { - const result = await roleService.updateRoles( - connection, - { color: role2.color }, - updateBody - ); - expect(result).toEqual(0); - }); + test('should return null when no role match the filter criteria', async () => { + const result = await roleService.updateRole(connection, { roleId: role1.roleId }, updateBody); + expect(result).toEqual(null); + }); + }); + + describe('updateRoles', () => { + const updateBody: IRoleUpdateBody = { + color: 111111, + }; + test('should update roles that match the filter criteria', async () => { + await insertRoles([role1, role2, role3], connection); + const result = await roleService.updateRoles(connection, { color: role2.color }, updateBody); + expect(result).toEqual(2); + const roleDoc1 = await roleService.getRole(connection, { roleId: role2.roleId }); + const roleDoc12 = await roleService.getRole(connection, { roleId: role3.roleId }); + expect(roleDoc1?.color).toBe(updateBody.color); + expect(roleDoc12?.color).toBe(updateBody.color); }); -}); \ No newline at end of file + test('should return 0 when no roles match the filter criteria', async () => { + const result = await roleService.updateRoles(connection, { color: role2.color }, updateBody); + expect(result).toEqual(0); + }); + }); +}); diff --git a/__tests__/utils/setupTestDB.ts b/__tests__/utils/setupTestDB.ts index f67c6463..029fae2e 100644 --- a/__tests__/utils/setupTestDB.ts +++ b/__tests__/utils/setupTestDB.ts @@ -1,19 +1,21 @@ -import mongoose from "mongoose"; -import config from "../../src/config"; +import mongoose from 'mongoose'; +import config from '../../src/config'; const setupTestDB = () => { - beforeAll(async () => { - mongoose.set("strictQuery", false); - await mongoose.connect(config.mongoose.serverURL); - }); + beforeAll(async () => { + mongoose.set('strictQuery', false); + await mongoose.connect(config.mongoose.serverURL); + }); - beforeEach(async () => { - await Promise.all(Object.values(mongoose.connection.collections).map(async (collection) => collection.deleteMany({}))); - }); + beforeEach(async () => { + await Promise.all( + Object.values(mongoose.connection.collections).map(async collection => collection.deleteMany({})) + ); + }); - afterAll(async () => { - await mongoose.disconnect(); - }); + afterAll(async () => { + await mongoose.disconnect(); + }); }; -export default setupTestDB; \ No newline at end of file +export default setupTestDB; diff --git a/bable.config.js b/bable.config.js index b58143a8..3147346e 100644 --- a/bable.config.js +++ b/bable.config.js @@ -1,2 +1,2 @@ // eslint-disable-next-line no-undef -module.exports = { presets: ['@babel/preset-env'] } \ No newline at end of file +module.exports = { presets: ['@babel/preset-env'] } diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index bbb2eacd..a80866c7 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,22 +1,22 @@ -version: "3.9" +version: '3.9' services: redis: - image: "redis:alpine" + image: 'redis:alpine' ports: - 6379:6379 mongo: - image: "mongo" + image: 'mongo' ports: - 27017:27017 environment: - MONGO_INITDB_ROOT_USERNAME=root - MONGO_INITDB_ROOT_PASSWORD=pass rabbitmq: - image: "rabbitmq:3-management-alpine" + image: 'rabbitmq:3-management-alpine' ports: - 5672:5672 - 15672:15672 environment: - RABBITMQ_DEFAULT_USER=root - - RABBITMQ_DEFAULT_PASS=pass \ No newline at end of file + - RABBITMQ_DEFAULT_PASS=pass diff --git a/docker-compose.test.yml b/docker-compose.test.yml index f56259d3..b3515553 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -1,4 +1,4 @@ -version: "3.9" +version: '3.9' services: app: @@ -8,30 +8,72 @@ services: dockerfile: Dockerfile environment: - PORT=3000 - - MONGODB_HOST=mongo - - MONGODB_PORT=27017 - - MONGODB_USER=root - - MONGODB_PASS=pass - - REDIS_QUEUE_HOST=redis - - REDIS_QUEUE_PORT=6379 - - RABBITMQ_HOST=localhost - - RABBITMQ_PORT=5672 - - RABBITMQ_USER=root - - RABBITMQ_PASS=pass + - DB_HOST=mongo + - DB_PORT=27017 + - DB_USER=root + - DB_PASSWORD=pass + - DB_NAME=discord-bot + - REDIS_HOST=redis + - REDIS_PORT=6379 + - REDIS_PASSWORD=twin + - RABBIT_HOST=localhost + - RABBIT_PORT=5672 + - RABBIT_USER=root + - RABBIT_PASSWORD=pass + - DISCROD_CLIENT_ID=123406245034481529638963 + - DISCORD_CLIENT_SECRET=hTgnafd0dndAJz6oYhpxNGp3hcasdf + - DISCORD_BOT_TOKEN=MTAasdf2MjQasf1MDQ4MTUyOTYzODk2Mw + - SENTRY_DSN=https://b9a3cf6fc9b849b6a9fdbd19dbb3a0eb@o4505110094282752.ingest.sentry.io/4505110103130112 + - SENTRY_ENV=test volumes: - ./coverage:/project/coverage depends_on: - - redis - - mongo + redis: + condition: service_healthy + mongo: + condition: service_healthy + rabbitmq: + condition: service_healthy + redis: - image: "redis:alpine" + image: 'redis:alpine' + healthcheck: + test: ["CMD", "redis-cli","ping"] + interval: 1m30s + timeout: 10s + retries: 2 + start_period: 40s + networks: + - discord_bot_network + mongo: - image: "mongo" + image: 'mongo' environment: - MONGO_INITDB_ROOT_USERNAME=root - MONGO_INITDB_ROOT_PASSWORD=pass + healthcheck: + test: echo 'db.stats().ok' | mongosh localhost:27017/test --quiet + interval: 60s + timeout: 10s + retries: 2 + start_period: 40s + networks: + - discord_bot_network + rabbitmq: - image: "rabbitmq:3-management-alpine" + image: 'rabbitmq:3-management-alpine' environment: - RABBITMQ_DEFAULT_USER=root - - RABBITMQ_DEFAULT_PASS=pass \ No newline at end of file + - RABBITMQ_DEFAULT_PASS=pass + healthcheck: + test: rabbitmq-diagnostics -q ping + interval: 30s + timeout: 30s + retries: 2 + start_period: 40s + networks: + - discord_bot_network + +networks: + discord_bot_network: + driver: bridge diff --git a/jest.config.js b/jest.config.js index e34f9740..cc37a1a3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,25 +1,20 @@ // eslint-disable-next-line no-undef module.exports = { - testPathIgnorePatterns: [ - "/__tests__/fixtures", - "/__tests__/utils", - ], - transform: { - '^.+\\.(ts|tsx)?$': 'ts-jest', - "^.+\\.(js|jsx)$": "babel-jest", - }, - collectCoverage: true, - testEnvironment: "node", - coverageReporters: ["json", "lcov", "text", "clover", "html"], - collectCoverageFrom: [ - "src/**/*.ts*" - ], - coverageThreshold: { - global: { - branches: 80, - functions: 80, - lines: 80, - statements: -10, - }, + testPathIgnorePatterns: ['/__tests__/fixtures', '/__tests__/utils'], + transform: { + '^.+\\.(ts|tsx)?$': 'ts-jest', + '^.+\\.(js|jsx)$': 'babel-jest' + }, + collectCoverage: true, + testEnvironment: 'node', + coverageReporters: ['json', 'lcov', 'text', 'clover', 'html'], + collectCoverageFrom: ['src/**/*.ts*'], + coverageThreshold: { + global: { + branches: 80, + functions: 80, + lines: 80, + statements: -10 } -}; \ No newline at end of file + } +} diff --git a/src/config/index.ts b/src/config/index.ts index 5f6758ed..def73ed7 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -20,7 +20,6 @@ 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(''), - }) .unknown(); @@ -33,12 +32,12 @@ export default { env: envVars.NODE_ENV, mongoose: { serverURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}/${envVars.DB_NAME}`, - dbURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}` + dbURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}`, }, redis: { host: envVars.REDIS_HOST, port: envVars.REDIS_PORT, - password: envVars.REDIS_PASSWORD + password: envVars.REDIS_PASSWORD, }, rabbitMQ: { url: `amqp://${envVars.RABBIT_USER}:${envVars.RABBIT_PASSWORD}@${envVars.RABBIT_HOST}:${envVars.RABBIT_PORT}`, diff --git a/src/database/connection.ts b/src/database/connection.ts index ce7729c6..84492961 100644 --- a/src/database/connection.ts +++ b/src/database/connection.ts @@ -6,10 +6,10 @@ import { Connection } from 'mongoose'; * @throws {MongooseError} - If there is an error closing the connection, it is logged to the console and the error is thrown. */ 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); - } -} \ No newline at end of file + 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); + } +} diff --git a/src/database/services/channel.service.ts b/src/database/services/channel.service.ts index cc78fecb..4ebb9764 100644 --- a/src/database/services/channel.service.ts +++ b/src/database/services/channel.service.ts @@ -1,4 +1,4 @@ -import { Connection } from 'mongoose'; +import { Connection, QueryOptions } from 'mongoose'; import { IChannel, IChannelMethods, IChannelUpdateBody } from '@togethercrew.dev/db'; /** @@ -8,12 +8,12 @@ import { IChannel, IChannelMethods, IChannelUpdateBody } from '@togethercrew.dev * @returns {Promise} - A promise that resolves to the created channel object. */ async function createChannel(connection: Connection, channel: IChannel): Promise { - try { - return await connection.models.Channel.create(channel); - } catch (error) { - console.log('Failed to create channel', error); - return null; - } + try { + return await connection.models.Channel.create(channel); + } catch (error) { + console.log('Failed to create channel', error); + return null; + } } /** @@ -23,12 +23,12 @@ async function createChannel(connection: Connection, channel: IChannel): Promise * @returns {Promise} - A promise that resolves to an array of the created channel objects. */ async function createChannels(connection: Connection, channels: IChannel[]): Promise { - try { - return await connection.models.Channel.insertMany(channels, { ordered: false }); - } catch (error) { - console.log('Failed to create channels', error); - return []; - } + try { + return await connection.models.Channel.insertMany(channels, { ordered: false }); + } catch (error) { + console.log('Failed to create channels', error); + return []; + } } /** @@ -38,12 +38,12 @@ async function createChannels(connection: Connection, channels: IChannel[]): Pro * @returns {Promise} - 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; - } + try { + return await connection.models.Channel.findOne(filter); + } catch (error) { + console.log('Failed to retrieve channel', error); + return null; + } } /** @@ -53,12 +53,12 @@ async function getChannel(connection: Connection, filter: object): Promise<(ICha * @returns {Promise} - A promise that resolves to an array of the matching channel objects. */ async function getChannels(connection: Connection, filter: object): Promise { - try { - return await connection.models.Channel.find(filter); - } catch (error) { - console.log('Failed to retrieve channels', error); - return []; - } + try { + return await connection.models.Channel.find(filter); + } catch (error) { + console.log('Failed to retrieve channels', error); + return []; + } } /** @@ -68,22 +68,45 @@ async function getChannels(connection: Connection, filter: object): Promise} - A promise that resolves to the updated channel object or null if not found. */ -async function updateChannel( - connection: Connection, - filter: object, - UpdateBody: IChannelUpdateBody +async function findOneChannelAndUpdate( + connection: Connection, + filter: object, + UpdateBody: IChannelUpdateBody ): Promise { - try { - const channel = await connection.models.Channel.findOne(filter); - if (!channel) { - return null; - } - Object.assign(channel, UpdateBody); - return await channel.save(); - } catch (error) { - console.log('Failed to update channel', error); - return null; + try { + const channel = await connection.models.Channel.findOne(filter); + if (!channel) { + return null; } + Object.assign(channel, UpdateBody); + return await channel.save(); + } catch (error) { + console.log('Failed to findOne channel and update', error); + return null; + } +} + +/** + * 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 {QueryOptions} options - An object containing the update option. + * @returns {Promise} - A promise that resolves to the updated channel object or null if not found. + */ +async function updateChannel( + connection: Connection, + filter: object, + UpdateBody: IChannelUpdateBody, + options: QueryOptions +) { + try { + const channel = await connection.models.Channel.updateOne(filter, UpdateBody, options); + return channel; + } catch (error) { + console.log('Failed to update channel', error); + return null; + } } /** @@ -93,25 +116,22 @@ async function updateChannel( * @param {IChannelUpdateBody} UpdateBody - An object containing the updated channel data. * @returns {Promise} - A promise that resolves to the number of updated channel entries. */ -async function updateChannels( - connection: Connection, - filter: object, - UpdateBody: IChannelUpdateBody -): Promise { - try { - const updateResult = await connection.models.Channel.updateMany(filter, UpdateBody); - return updateResult.modifiedCount || 0; - } catch (error) { - console.log('Failed to update channels', error); - return 0; - } +async function updateChannels(connection: Connection, filter: object, UpdateBody: IChannelUpdateBody): Promise { + try { + const updateResult = await connection.models.Channel.updateMany(filter, UpdateBody); + return updateResult.modifiedCount || 0; + } catch (error) { + console.log('Failed to update channels', error); + return 0; + } } export default { - createChannel, - createChannels, - updateChannel, - getChannel, - getChannels, - updateChannels, -}; \ No newline at end of file + createChannel, + createChannels, + findOneChannelAndUpdate, + updateChannel, + getChannel, + getChannels, + updateChannels, +}; diff --git a/src/database/services/guildMember.service.ts b/src/database/services/guildMember.service.ts index 0a9f26cf..e6edc52c 100644 --- a/src/database/services/guildMember.service.ts +++ b/src/database/services/guildMember.service.ts @@ -37,7 +37,10 @@ async function createGuildMembers(connection: Connection, guildMembers: IGuildMe * @param {object} filter - An object specifying the filter criteria to match the desired guild member entry. * @returns {Promise} - A promise that resolves to the matching guild member object or null if not found. */ -async function getGuildMember(connection: Connection, filter: object): Promise<(IGuildMember & IGuildMemberMethods) | null> { +async function getGuildMember( + connection: Connection, + filter: object +): Promise<(IGuildMember & IGuildMemberMethods) | null> { try { return await connection.models.GuildMember.findOne(filter); } catch (error) { diff --git a/src/database/services/index.ts b/src/database/services/index.ts index b8440660..5d7a9d19 100644 --- a/src/database/services/index.ts +++ b/src/database/services/index.ts @@ -3,4 +3,4 @@ import rawInfoService from './rawInfo.service'; import guildService from './guild.service'; import roleService from './role.service'; import channelService from './channel.service'; -export { guildMemberService, guildService, rawInfoService, roleService, channelService }; \ No newline at end of file +export { guildMemberService, guildService, rawInfoService, roleService, channelService }; diff --git a/src/database/services/role.service.ts b/src/database/services/role.service.ts index bd98f928..2a204e18 100644 --- a/src/database/services/role.service.ts +++ b/src/database/services/role.service.ts @@ -8,12 +8,12 @@ import { IRole, IRoleMethods, IRoleUpdateBody } from '@togethercrew.dev/db'; * @returns {Promise} - A promise that resolves to the created role object. */ async function createRole(connection: Connection, role: IRole): Promise { - try { - return await connection.models.Role.create(role); - } catch (error) { - console.log('Failed to create role', error); - return null; - } + try { + return await connection.models.Role.create(role); + } catch (error) { + console.log('Failed to create role', error); + return null; + } } /** @@ -23,12 +23,12 @@ async function createRole(connection: Connection, role: IRole): Promise} - A promise that resolves to an array of the created role objects. */ async function createRoles(connection: Connection, roles: IRole[]): Promise { - try { - return await connection.models.Role.insertMany(roles, { ordered: false }); - } catch (error) { - console.log('Failed to create roles', error); - return []; - } + try { + return await connection.models.Role.insertMany(roles, { ordered: false }); + } catch (error) { + console.log('Failed to create roles', error); + return []; + } } /** @@ -38,12 +38,12 @@ async function createRoles(connection: Connection, roles: IRole[]): Promise} - A promise that resolves to the matching role object or null if not found. */ async function getRole(connection: Connection, filter: object): Promise<(IRole & IRoleMethods) | null> { - try { - return await connection.models.Role.findOne(filter); - } catch (error) { - console.log('Failed to retrieve role', error); - return null; - } + try { + return await connection.models.Role.findOne(filter); + } catch (error) { + console.log('Failed to retrieve role', error); + return null; + } } /** @@ -53,12 +53,12 @@ async function getRole(connection: Connection, filter: object): Promise<(IRole & * @returns {Promise} - A promise that resolves to an array of the matching role objects. */ async function getRoles(connection: Connection, filter: object): Promise { - try { - return await connection.models.Role.find(filter); - } catch (error) { - console.log('Failed to retrieve roles', error); - return []; - } + try { + return await connection.models.Role.find(filter); + } catch (error) { + console.log('Failed to retrieve roles', error); + return []; + } } /** @@ -68,22 +68,18 @@ async function getRoles(connection: Connection, filter: object): Promise} - A promise that resolves to the updated role object or null if not found. */ -async function updateRole( - connection: Connection, - filter: object, - UpdateBody: IRoleUpdateBody -): Promise { - try { - const role = await connection.models.Role.findOne(filter); - if (!role) { - return null; - } - Object.assign(role, UpdateBody); - return await role.save(); - } catch (error) { - console.log('Failed to update role', error); - return null; +async function updateRole(connection: Connection, filter: object, UpdateBody: IRoleUpdateBody): Promise { + try { + const role = await connection.models.Role.findOne(filter); + if (!role) { + return null; } + Object.assign(role, UpdateBody); + return await role.save(); + } catch (error) { + console.log('Failed to update role', error); + return null; + } } /** @@ -93,25 +89,21 @@ async function updateRole( * @param {IRoleUpdateBody} UpdateBody - An object containing the updated role data. * @returns {Promise} - A promise that resolves to the number of updated role entries. */ -async function updateRoles( - connection: Connection, - filter: object, - UpdateBody: IRoleUpdateBody -): Promise { - try { - const updateResult = await connection.models.Role.updateMany(filter, UpdateBody); - return updateResult.modifiedCount || 0; - } catch (error) { - console.log('Failed to update roles', error); - return 0; - } +async function updateRoles(connection: Connection, filter: object, UpdateBody: IRoleUpdateBody): Promise { + try { + const updateResult = await connection.models.Role.updateMany(filter, UpdateBody); + return updateResult.modifiedCount || 0; + } catch (error) { + console.log('Failed to update roles', error); + return 0; + } } export default { - createRole, - createRoles, - updateRole, - getRole, - getRoles, - updateRoles, -}; \ No newline at end of file + createRole, + createRoles, + updateRole, + getRole, + getRoles, + updateRoles, +}; diff --git a/src/events/channel/channelCreate.ts b/src/events/channel/channelCreate.ts index d7b89f20..8f9ba152 100644 --- a/src/events/channel/channelCreate.ts +++ b/src/events/channel/channelCreate.ts @@ -5,28 +5,28 @@ import config from '../../config'; import { closeConnection } from '../../database/connection'; export default { - name: Events.ChannelCreate, - once: false, - async execute(channel: Channel) { - try { - if (channel instanceof GuildChannel && channel instanceof TextChannel) { - const connection = databaseService.connectionFactory(channel.guildId, config.mongoose.dbURL); - await channelService.createChannel(connection, { - channelId: channel.id, - name: channel.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(), - })) - }) - await closeConnection(connection) - } - } catch (err) { - // TODO: improve error handling - console.log(err); - } - }, + name: Events.ChannelCreate, + once: false, + async execute(channel: Channel) { + try { + if (channel instanceof GuildChannel && channel instanceof TextChannel) { + const connection = databaseService.connectionFactory(channel.guildId, config.mongoose.dbURL); + await channelService.createChannel(connection, { + channelId: channel.id, + name: channel.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(), + })), + }); + await closeConnection(connection); + } + } catch (err) { + // TODO: improve error handling + console.log(err); + } + }, }; diff --git a/src/events/channel/channelDelete.ts b/src/events/channel/channelDelete.ts index 614f14ba..d7cc8260 100644 --- a/src/events/channel/channelDelete.ts +++ b/src/events/channel/channelDelete.ts @@ -5,19 +5,19 @@ import config from '../../config'; import { closeConnection } from '../../database/connection'; export default { - name: Events.ChannelDelete, - once: false, - async execute(channel: Channel) { - try { - if (channel instanceof GuildChannel && channel instanceof TextChannel) { - const connection = databaseService.connectionFactory(channel.guildId, config.mongoose.dbURL); - const channelDoc = await channelService.getChannel(connection, { channelId: channel.id }); - await channelDoc?.softDelete(); - await closeConnection(connection) - } - } catch (err) { - // TODO: improve error handling - console.log(err); - } - }, + name: Events.ChannelDelete, + once: false, + async execute(channel: Channel) { + try { + if (channel instanceof GuildChannel && channel instanceof TextChannel) { + const connection = databaseService.connectionFactory(channel.guildId, config.mongoose.dbURL); + const channelDoc = await channelService.getChannel(connection, { channelId: channel.id }); + await channelDoc?.softDelete(); + await closeConnection(connection); + } + } catch (err) { + // TODO: improve error handling + console.log(err); + } + }, }; diff --git a/src/events/channel/channelUpdate.ts b/src/events/channel/channelUpdate.ts index a92a1f09..9426bb44 100644 --- a/src/events/channel/channelUpdate.ts +++ b/src/events/channel/channelUpdate.ts @@ -5,43 +5,41 @@ import config from '../../config'; import { closeConnection } from '../../database/connection'; export default { - name: Events.ChannelUpdate, - once: false, - async execute(oldChannel: Channel, newChannel: Channel) { - try { - if (oldChannel instanceof GuildChannel && oldChannel instanceof TextChannel && newChannel instanceof GuildChannel && newChannel instanceof TextChannel) { - const connection = databaseService.connectionFactory(oldChannel.guildId, config.mongoose.dbURL); - const channel = await channelService.updateChannel(connection, - { channelId: oldChannel.id }, - { - name: newChannel.name, - parentId: newChannel.parentId, - permissionOverwrites: Array.from(newChannel.permissionOverwrites.cache.values()).map(overwrite => ({ - id: overwrite.id, - type: overwrite.type, - allow: overwrite.allow.bitfield.toString(), - deny: overwrite.deny.bitfield.toString(), - })) - } - ); - if (!channel) { - await channelService.createChannel(connection, { - channelId: newChannel.id, - name: newChannel.name, - parentId: newChannel.parentId, - permissionOverwrites: Array.from(newChannel.permissionOverwrites.cache.values()).map(overwrite => ({ - id: overwrite.id, - type: overwrite.type, - allow: overwrite.allow.bitfield.toString(), - deny: overwrite.deny.bitfield.toString(), - })) - }) - } - await closeConnection(connection) - } - } catch (err) { - // TODO: improve error handling - console.log(err); - } - }, + name: Events.ChannelUpdate, + once: false, + async execute(oldChannel: Channel, newChannel: Channel) { + try { + if ( + oldChannel instanceof GuildChannel && + oldChannel instanceof TextChannel && + newChannel instanceof GuildChannel && + newChannel instanceof TextChannel + ) { + const connection = databaseService.connectionFactory(oldChannel.guildId, config.mongoose.dbURL); + + await channelService.updateChannel( + connection, + { channelId: oldChannel.id }, + { + channelId: oldChannel.id, + name: newChannel.name, + parentId: newChannel.parentId, + permissionOverwrites: Array.from(newChannel.permissionOverwrites.cache.values()).map(overwrite => ({ + id: overwrite.id, + type: overwrite.type, + allow: overwrite.allow.bitfield.toString(), + deny: overwrite.deny.bitfield.toString(), + })), + // TODO: any should be removed once we update @togethercrew.dev/db + } as any, + { upsert: true } + ); + + await closeConnection(connection); + } + } catch (err) { + // TODO: improve error handling + console.log(err); + } + }, }; diff --git a/src/events/client/ready.ts b/src/events/client/ready.ts index 62f130a8..0a7d5259 100644 --- a/src/events/client/ready.ts +++ b/src/events/client/ready.ts @@ -1,5 +1,5 @@ import { Events, Client } from 'discord.js'; -import { databaseService } from '@togethercrew.dev/db' +import { databaseService } from '@togethercrew.dev/db'; import { guildService } from '../../database/services'; import fetchMembers from '../../functions/fetchMembers'; import fetchChannels from '../../functions/fetchChannels'; @@ -11,22 +11,22 @@ export default { name: Events.ClientReady, once: true, async execute(client: Client) { - console.log(`client READY event is running`) + console.log(`client READY event is running`); const guilds = await guildService.getGuilds({ isDisconnected: false }); for (let i = 0; i < guilds.length; i++) { const connection = databaseService.connectionFactory(guilds[i].guildId, config.mongoose.dbURL); - console.log(`client READY: fetch members is running for ${guilds[i].guildId}:${guilds[i].name}`) - await fetchMembers(connection, client, guilds[i].guildId) - console.log(`client READY: fetch members is Done ${guilds[i].guildId}:${guilds[i].name}`) + console.log(`client READY: fetch members is running for ${guilds[i].guildId}:${guilds[i].name}`); + await fetchMembers(connection, client, guilds[i].guildId); + console.log(`client READY: fetch members is Done ${guilds[i].guildId}:${guilds[i].name}`); - console.log(`client READY: fetch roles is running for ${guilds[i].guildId}:${guilds[i].name}`) - await fetchRoles(connection, client, guilds[i].guildId) - console.log(`client READY: fetch roles is Done ${guilds[i].guildId}:${guilds[i].name}`) + console.log(`client READY: fetch roles is running for ${guilds[i].guildId}:${guilds[i].name}`); + await fetchRoles(connection, client, guilds[i].guildId); + console.log(`client READY: fetch roles is Done ${guilds[i].guildId}:${guilds[i].name}`); - console.log(`client READY: fetch channels is running for ${guilds[i].guildId}:${guilds[i].name}`) - await fetchChannels(connection, client, guilds[i].guildId) - console.log(`client READY: fetch channels is Done ${guilds[i].guildId}:${guilds[i].name}`) - await closeConnection(connection) + console.log(`client READY: fetch channels is running for ${guilds[i].guildId}:${guilds[i].name}`); + await fetchChannels(connection, client, guilds[i].guildId); + console.log(`client READY: fetch channels is Done ${guilds[i].guildId}:${guilds[i].name}`); + await closeConnection(connection); } }, -}; \ No newline at end of file +}; diff --git a/src/events/member/guildMemberAdd.ts b/src/events/member/guildMemberAdd.ts index 0415b960..cc184159 100644 --- a/src/events/member/guildMemberAdd.ts +++ b/src/events/member/guildMemberAdd.ts @@ -23,11 +23,10 @@ export default { discriminator: member.user.discriminator, deletedAt: null, permissions: member.permissions.bitfield.toString(), - nickname: member.nickname + nickname: member.nickname, } ); - } - else { + } else { await guildMemberService.createGuildMember(connection, { discordId: member.user.id, username: member.user.username, @@ -37,13 +36,13 @@ export default { isBot: member.user.bot, discriminator: member.user.discriminator, permissions: member.permissions.bitfield.toString(), - nickname: member.nickname + nickname: member.nickname, }); } - await closeConnection(connection) + await closeConnection(connection); } catch (err) { // TODO: improve error handling console.log(err); } }, -}; \ No newline at end of file +}; diff --git a/src/events/member/guildMemberRemove.ts b/src/events/member/guildMemberRemove.ts index b0c6438e..beeb105a 100644 --- a/src/events/member/guildMemberRemove.ts +++ b/src/events/member/guildMemberRemove.ts @@ -5,18 +5,17 @@ import config from '../../config'; import { closeConnection } from '../../database/connection'; export default { - name: Events.GuildMemberRemove, - once: false, - async execute(member: GuildMember) { - try { - const connection = databaseService.connectionFactory(member.guild.id, config.mongoose.dbURL); - const guildMemberDoc = await guildMemberService.getGuildMember(connection, { discordId: member.user.id }); - await guildMemberDoc?.softDelete(); - await closeConnection(connection) - - } catch (err) { - // TODO: improve error handling - console.log(err); - } - }, + name: Events.GuildMemberRemove, + once: false, + async execute(member: GuildMember) { + try { + const connection = databaseService.connectionFactory(member.guild.id, config.mongoose.dbURL); + const guildMemberDoc = await guildMemberService.getGuildMember(connection, { discordId: member.user.id }); + await guildMemberDoc?.softDelete(); + await closeConnection(connection); + } catch (err) { + // TODO: improve error handling + console.log(err); + } + }, }; diff --git a/src/events/member/guildMemberUpdate.ts b/src/events/member/guildMemberUpdate.ts index a36edfad..e201b400 100644 --- a/src/events/member/guildMemberUpdate.ts +++ b/src/events/member/guildMemberUpdate.ts @@ -36,10 +36,10 @@ export default { permissions: newMember.permissions.bitfield.toString(), }); } - await closeConnection(connection) + await closeConnection(connection); } catch (err) { // TODO: improve error handling console.log(err); } }, -}; \ No newline at end of file +}; diff --git a/src/events/role/roleCreate.ts b/src/events/role/roleCreate.ts index b02535fb..2d37133a 100644 --- a/src/events/role/roleCreate.ts +++ b/src/events/role/roleCreate.ts @@ -5,21 +5,20 @@ import config from '../../config'; import { closeConnection } from '../../database/connection'; export default { - name: Events.GuildRoleCreate, - once: false, - async execute(role: Role) { - try { - const connection = databaseService.connectionFactory(role.guild.id, config.mongoose.dbURL); - await roleService.createRole(connection, { - roleId: role.id, - name: role.name, - color: role.color - }) - await closeConnection(connection) - - } catch (err) { - // TODO: improve error handling - console.log(err); - } - }, + name: Events.GuildRoleCreate, + once: false, + async execute(role: Role) { + try { + const connection = databaseService.connectionFactory(role.guild.id, config.mongoose.dbURL); + await roleService.createRole(connection, { + roleId: role.id, + name: role.name, + color: role.color, + }); + await closeConnection(connection); + } catch (err) { + // TODO: improve error handling + console.log(err); + } + }, }; diff --git a/src/events/role/roleDelete.ts b/src/events/role/roleDelete.ts index d24d7e71..3101f8f8 100644 --- a/src/events/role/roleDelete.ts +++ b/src/events/role/roleDelete.ts @@ -5,18 +5,17 @@ import config from '../../config'; import { closeConnection } from '../../database/connection'; export default { - name: Events.GuildRoleDelete, - once: false, - async execute(role: Role) { - try { - const connection = databaseService.connectionFactory(role.guild.id, config.mongoose.dbURL); - const roleDoc = await roleService.getRole(connection, { roleId: role.id }); - await roleDoc?.softDelete(); - await closeConnection(connection) - - } catch (err) { - // TODO: improve error handling - console.log(err); - } - }, + name: Events.GuildRoleDelete, + once: false, + async execute(role: Role) { + try { + const connection = databaseService.connectionFactory(role.guild.id, config.mongoose.dbURL); + const roleDoc = await roleService.getRole(connection, { roleId: role.id }); + await roleDoc?.softDelete(); + await closeConnection(connection); + } catch (err) { + // TODO: improve error handling + console.log(err); + } + }, }; diff --git a/src/events/role/roleUpdate.ts b/src/events/role/roleUpdate.ts index 122e64f7..b14336c5 100644 --- a/src/events/role/roleUpdate.ts +++ b/src/events/role/roleUpdate.ts @@ -5,27 +5,27 @@ import config from '../../config'; import { closeConnection } from '../../database/connection'; export default { - name: Events.GuildRoleUpdate, - once: false, - async execute(oldRole: Role, newRole: Role) { - try { - const connection = databaseService.connectionFactory(oldRole.guild.id, config.mongoose.dbURL); - const role = await roleService.updateRole(connection, - { roleId: oldRole.id }, - { name: newRole.name, color: newRole.color } - ); - if (!role) { - await roleService.createRole(connection, { - roleId: newRole.id, - name: newRole.name, - color: newRole.color - }) - } - await closeConnection(connection) - - } catch (err) { - // TODO: improve error handling - console.log(err); - } - }, + name: Events.GuildRoleUpdate, + once: false, + async execute(oldRole: Role, newRole: Role) { + try { + const connection = databaseService.connectionFactory(oldRole.guild.id, config.mongoose.dbURL); + const role = await roleService.updateRole( + connection, + { roleId: oldRole.id }, + { name: newRole.name, color: newRole.color } + ); + if (!role) { + await roleService.createRole(connection, { + roleId: newRole.id, + name: newRole.name, + color: newRole.color, + }); + } + await closeConnection(connection); + } catch (err) { + // TODO: improve error handling + console.log(err); + } + }, }; diff --git a/src/functions/cronJon.ts b/src/functions/cronJon.ts index c85f7d0b..ed373f36 100644 --- a/src/functions/cronJon.ts +++ b/src/functions/cronJon.ts @@ -1,20 +1,20 @@ import { Client, Snowflake } from 'discord.js'; import { guildService } from '../database/services'; import { databaseService } from '@togethercrew.dev/db'; -import { ChoreographyDict, MBConnection, Status } from "@togethercrew.dev/tc-messagebroker" +import { ChoreographyDict, MBConnection, Status } from '@togethercrew.dev/tc-messagebroker'; import config from '../config'; -import guildExtraction from './guildExtraction' +import guildExtraction from './guildExtraction'; import { closeConnection } from '../database/connection'; async function createAndStartCronJobSaga(guildId: Snowflake) { - console.log("[createAndStartCronJobSaga]") - const saga = await MBConnection.models.Saga.create({ - status: Status.NOT_STARTED, - data: { guildId }, - choreography: ChoreographyDict.DISCORD_SCHEDULED_JOB - }) - console.log("[SAGA] ", saga) - await saga.start() + console.log('[createAndStartCronJobSaga]'); + const saga = await MBConnection.models.Saga.create({ + status: Status.NOT_STARTED, + data: { guildId }, + choreography: ChoreographyDict.DISCORD_SCHEDULED_JOB, + }); + console.log('[SAGA] ', saga); + await saga.start(); } /** @@ -22,17 +22,17 @@ async function createAndStartCronJobSaga(guildId: Snowflake) { * @param {Client} client - The discord.js client object used to fetch the guilds. */ export default async function cronJob(client: Client) { - try { - const guilds = await guildService.getGuilds({ isDisconnected: false }); - for (let i = 0; i < guilds.length; i++) { - console.log(`Cron JOB is running for ${guilds[i].guildId}:${guilds[i].name}`) - const connection = databaseService.connectionFactory(guilds[i].guildId, config.mongoose.dbURL); - await guildExtraction(connection, client, guilds[i].guildId) - await createAndStartCronJobSaga(guilds[i].guildId) - console.log(`Cron JOB is Done ${guilds[i].guildId}:${guilds[i].name}`) - await closeConnection(connection) - } - } catch (err) { - console.log('Cron job failed', err) + try { + const guilds = await guildService.getGuilds({ isDisconnected: false }); + for (let i = 0; i < guilds.length; i++) { + console.log(`Cron JOB is running for ${guilds[i].guildId}:${guilds[i].name}`); + const connection = databaseService.connectionFactory(guilds[i].guildId, config.mongoose.dbURL); + await guildExtraction(connection, client, guilds[i].guildId); + await createAndStartCronJobSaga(guilds[i].guildId); + console.log(`Cron JOB is Done ${guilds[i].guildId}:${guilds[i].name}`); + await closeConnection(connection); } -} \ No newline at end of file + } catch (err) { + console.log('Cron job failed', err); + } +} diff --git a/src/functions/fetchChannels.ts b/src/functions/fetchChannels.ts index da0d6fb6..e120d7cb 100644 --- a/src/functions/fetchChannels.ts +++ b/src/functions/fetchChannels.ts @@ -4,57 +4,62 @@ import { IChannel } from '@togethercrew.dev/db'; import { channelService, guildService } from '../database/services'; /** -* Extracts necessary data from a given channel. -* @param {Array} channelArray - An array of channels from which data is to be extracted. -* @returns {IChannel} - The extracted data in the form of an IChannel object. -*/ + * Extracts necessary data from a given channel. + * @param {Array} channelArray - An array of channels from which data is to be extracted. + * @returns {IChannel} - The extracted data in the form of an IChannel object. + */ function getNeedDataFromChannel(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(), - })) - }; + 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(), + })), + }; } /** -* Iterates over a list of channels and pushes extracted data from each channel to an array. -* @param {IChannel[]} arr - The array to which extracted data will be pushed. -* @param {Array} channelArray - An array of channels from which data is to be extracted. -* @returns {IChannel[]} - The updated array containing the extracted data. -*/ -function pushChannelsToArray(arr: IChannel[], channelArray: Array): IChannel[] { - for (const channel of channelArray) { - arr.push(getNeedDataFromChannel(channel)); - } - return arr; + * Iterates over a list of channels and pushes extracted data from each channel to an array. + * @param {IChannel[]} arr - The array to which extracted data will be pushed. + * @param {Array} channelArray - An array of channels from which data is to be extracted. + * @returns {IChannel[]} - The updated array containing the extracted data. + */ +function pushChannelsToArray( + arr: IChannel[], + channelArray: Array +): IChannel[] { + for (const channel of channelArray) { + arr.push(getNeedDataFromChannel(channel)); + } + return arr; } /** -* Fetches and saves text and voice channel information from a given guild. -* @param {Connection} connection - Mongoose connection object for the database. -* @param {Client} client - The discord.js client object used to fetch the guild. -* @param {Snowflake} guildId - The identifier of the guild to extract text and voice channels from. -*/ + * Fetches and saves text and voice channel information from a given guild. + * @param {Connection} connection - Mongoose connection object for the database. + * @param {Client} client - The discord.js client object used to fetch the guild. + * @param {Snowflake} guildId - The identifier of the guild to extract text and voice channels from. + */ export default async function fetchGuildChannels(connection: Connection, client: Client, guildId: Snowflake) { - console.log(`Fetching text and voice channels for guild: ${guildId}`) - try { - if (!client.guilds.cache.has(guildId)) { - await guildService.updateGuild({ guildId }, { isDisconnected: false }) - return - } - const guild = await client.guilds.fetch(guildId); - const channelsToStore: IChannel[] = []; - const textAndVoiceChannels = [...guild.channels.cache.values()].filter(channel => channel.type === 0 || channel.type === 2 || channel.type === 4) as Array; - pushChannelsToArray(channelsToStore, textAndVoiceChannels); - await channelService.createChannels(connection, channelsToStore); // assuming a 'channelService' - } catch (err) { - console.error(`Failed to fetch text and voice channels of guild ${guildId}`, err); + console.log(`Fetching text and voice channels for guild: ${guildId}`); + try { + if (!client.guilds.cache.has(guildId)) { + await guildService.updateGuild({ guildId }, { isDisconnected: false }); + return; } - console.log(`Completed fetching text and voice channels for guild: ${guildId}`) -} \ No newline at end of file + const guild = await client.guilds.fetch(guildId); + const channelsToStore: IChannel[] = []; + const textAndVoiceChannels = [...guild.channels.cache.values()].filter( + channel => channel.type === 0 || channel.type === 2 || channel.type === 4 + ) as Array; + pushChannelsToArray(channelsToStore, textAndVoiceChannels); + await channelService.createChannels(connection, channelsToStore); // assuming a 'channelService' + } catch (err) { + console.error(`Failed to fetch text and voice channels of guild ${guildId}`, err); + } + console.log(`Completed fetching text and voice channels for guild: ${guildId}`); +} diff --git a/src/functions/fetchMembers.ts b/src/functions/fetchMembers.ts index 1eae8b85..73ad6c45 100644 --- a/src/functions/fetchMembers.ts +++ b/src/functions/fetchMembers.ts @@ -4,43 +4,38 @@ import { IGuildMember } from '@togethercrew.dev/db'; import { guildMemberService, guildService } from '../database/services'; /** -* Extracts necessary data from a given guild member. -* @param {IGuildMember} guildMember - The guild member object from which data is to be extracted. -* @returns {Promise} - A promise that resolves to an object of type IRawInfo containing the extracted data. -*/ + * Extracts necessary data from a given guild member. + * @param {IGuildMember} guildMember - The guild member object from which data is to be extracted. + * @returns {Promise} - A promise that resolves to an object of type IRawInfo containing the extracted data. + */ function getNeedDataFromGuildMember(guildMember: GuildMember): IGuildMember { - return { - discordId: guildMember.user.id, - username: guildMember.user.username, - avatar: guildMember.user.avatar, - joinedAt: guildMember.joinedAt, - roles: guildMember.roles.cache.map(role => role.id), - isBot: guildMember.user.bot, - discriminator: guildMember.user.discriminator, - permissions: guildMember.permissions.bitfield.toString(), - nickname: guildMember.nickname - }; + return { + discordId: guildMember.user.id, + username: guildMember.user.username, + avatar: guildMember.user.avatar, + joinedAt: guildMember.joinedAt, + roles: guildMember.roles.cache.map(role => role.id), + isBot: guildMember.user.bot, + discriminator: guildMember.user.discriminator, + permissions: guildMember.permissions.bitfield.toString(), + nickname: guildMember.nickname, + }; } - /** -* Iterates over a list of guild members and pushes extracted data from each guild member to an array. -* @param {Connection} connection - Mongoose connection object for the database. -* @param {IRawInfo[]} arr - The array to which extracted data will be pushed. -* @param {GuildMember[]} guildMembersArray - An array of guild members from which data is to be extracted. -* @returns {Promise} - A promise that resolves to the updated array containing the extracted data. -*/ -function pushMembersToArray( - arr: IGuildMember[], - guildMembersArray: GuildMember[], -): IGuildMember[] { - for (const guildMember of guildMembersArray) { - arr.push(getNeedDataFromGuildMember(guildMember)); - } - return arr; + * Iterates over a list of guild members and pushes extracted data from each guild member to an array. + * @param {Connection} connection - Mongoose connection object for the database. + * @param {IRawInfo[]} arr - The array to which extracted data will be pushed. + * @param {GuildMember[]} guildMembersArray - An array of guild members from which data is to be extracted. + * @returns {Promise} - A promise that resolves to the updated array containing the extracted data. + */ +function pushMembersToArray(arr: IGuildMember[], guildMembersArray: GuildMember[]): IGuildMember[] { + for (const guildMember of guildMembersArray) { + arr.push(getNeedDataFromGuildMember(guildMember)); + } + return arr; } - /** * Extracts information from a given guild. * @param {Connection} connection - Mongoose connection object for the database. @@ -48,20 +43,19 @@ function pushMembersToArray( * @param {Snowflake} guildId - The identifier of the guild to extract information from. */ export default async function fetchGuildMembers(connection: Connection, client: Client, guildId: Snowflake) { - console.log(`Fetching members for guild: ${guildId}`) - try { - if (!client.guilds.cache.has(guildId)) { - await guildService.updateGuild({ guildId }, { isDisconnected: false }) - return - } - const guild = await client.guilds.fetch(guildId); - const membersToStore: IGuildMember[] = []; - const fetchMembers = await guild.members.fetch(); - pushMembersToArray(membersToStore, [...fetchMembers.values()]) - await guildMemberService.createGuildMembers(connection, membersToStore); - - } catch (err) { - console.error(`Failed to fetch members of guild ${guildId}`, err); + console.log(`Fetching members for guild: ${guildId}`); + try { + if (!client.guilds.cache.has(guildId)) { + await guildService.updateGuild({ guildId }, { isDisconnected: false }); + return; } - console.log(`Completed fetching members for guild: ${guildId}`) -} \ No newline at end of file + const guild = await client.guilds.fetch(guildId); + const membersToStore: IGuildMember[] = []; + const fetchMembers = await guild.members.fetch(); + pushMembersToArray(membersToStore, [...fetchMembers.values()]); + await guildMemberService.createGuildMembers(connection, membersToStore); + } catch (err) { + console.error(`Failed to fetch members of guild ${guildId}`, err); + } + console.log(`Completed fetching members for guild: ${guildId}`); +} diff --git a/src/functions/fetchMessages.ts b/src/functions/fetchMessages.ts index e9d86f11..3cd63076 100644 --- a/src/functions/fetchMessages.ts +++ b/src/functions/fetchMessages.ts @@ -49,43 +49,49 @@ async function getReactions(message: Message): Promise { * @returns {Promise} - A promise that resolves to an object of type IRawInfo containing the extracted data. */ async function getNeedDataFromMessage(message: Message, threadInfo?: threadInfo): Promise { - if (threadInfo) { - return { - type: message.type, - author: message.author.id, - content: message.content, - createdDate: message.createdAt, - role_mentions: message.mentions.roles.map((role: Role) => role.id), - user_mentions: message.mentions.users.map((user: User) => user.id), - replied_user: message.type === 19 ? message.mentions.repliedUser?.id : null, - reactions: await getReactions(message), - messageId: message.id, - channelId: threadInfo?.channelId ? threadInfo?.channelId : '', - channelName: threadInfo?.channelName ? threadInfo?.channelName : '', - threadId: threadInfo?.threadId ? threadInfo?.threadId : null, - threadName: threadInfo?.threadName ? threadInfo?.threadName : null, - }; - } else { - return { - type: message.type, - author: message.author.id, - content: message.content, - createdDate: message.createdAt, - role_mentions: message.mentions.roles.map((role: Role) => role.id), - user_mentions: message.mentions.users.map((user: User) => user.id), - replied_user: message.type === 19 ? message.mentions.repliedUser?.id : null, - reactions: await getReactions(message), - messageId: message.id, - channelId: message.channelId, - channelName: message.channel instanceof TextChannel ? message.channel.name : null, - threadId: null, - threadName: null, - }; - } - + const type = message.type; + const author = message.author.id; + const content = message.content; + const createdDate = message.createdAt; + const role_mentions = message.mentions.roles.map((role: Role) => role.id); + const user_mentions = message.mentions.users.map((user: User) => user.id); + const replied_user = message.type === 19 ? message.mentions.repliedUser?.id : null; + const reactions = await getReactions(message); + const messageId = message.id; + + let channelId; + if (threadInfo) channelId = threadInfo?.channelId ? threadInfo?.channelId : ''; + else channelId = message.channelId; + + let channelName; + if (threadInfo) channelName = threadInfo?.channelName ? threadInfo?.channelName : ''; + else channelName = message.channel instanceof TextChannel ? message.channel.name : null; + + let threadId; + if (threadInfo) threadId = threadInfo?.threadId ? threadInfo?.threadId : null; + else threadId = null; + + let threadName; + if (threadInfo) threadName = threadInfo?.threadName ? threadInfo?.threadName : null; + else threadName = null; + + return { + type, + author, + content, + createdDate, + role_mentions, + user_mentions, + replied_user, + reactions, + messageId, + channelId, + channelName, + threadId, + threadName, + }; } - /** * Iterates over a list of messages and pushes extracted data from each message to an array. * @param {Connection} connection - Mongoose connection object for the database. @@ -130,7 +136,7 @@ async function fetchMessages( fetchDirection: 'before' | 'after' = 'before' ) { try { - console.log(`fetch msgs is running for ${channel.name}: ${channel.id}`) + console.log(`fetch msgs is running for ${channel.name}: ${channel.id}`); const messagesToStore: IRawInfo[] = []; const options: FetchOptions = { limit: 10 }; if (rawInfo) { @@ -140,39 +146,30 @@ async function fetchMessages( while (fetchedMessages.size > 0) { const boundaryMessage = fetchDirection === 'before' ? fetchedMessages.last() : fetchedMessages.first(); + const isBoundaryMessage = !boundaryMessage || (period && boundaryMessage.createdAt < period); - if (!boundaryMessage || (period && boundaryMessage.createdAt < period)) { - if (period) { - fetchedMessages = fetchedMessages.filter(msg => msg.createdAt > period); - } - channel instanceof ThreadChannel - ? await pushMessagesToArray(connection, messagesToStore, [...fetchedMessages.values()], { + if (isBoundaryMessage && period) fetchedMessages = fetchedMessages.filter(msg => msg.createdAt > period); + + channel instanceof ThreadChannel + ? await pushMessagesToArray(connection, messagesToStore, [...fetchedMessages.values()], { threadId: channel.id, threadName: channel.name, channelId: channel.parent?.id, channelName: channel.parent?.name, }) - : await pushMessagesToArray(connection, messagesToStore, [...fetchedMessages.values()]); - break; - } - - channel instanceof ThreadChannel - ? await pushMessagesToArray(connection, messagesToStore, [...fetchedMessages.values()], { - threadId: channel.id, - threadName: channel.name, - channelId: channel.parent?.id, - channelName: channel.parent?.name, - }) : await pushMessagesToArray(connection, messagesToStore, [...fetchedMessages.values()]); - options[fetchDirection] = boundaryMessage.id; - fetchedMessages = await channel.messages.fetch(options); + + if (isBoundaryMessage) break; + else { + options[fetchDirection] = boundaryMessage.id; + fetchedMessages = await channel.messages.fetch(options); + } } await rawInfoService.createRawInfos(connection, messagesToStore); - console.log(`fetch msgs is done for ${channel.name}: ${channel.id}`) + console.log(`fetch msgs is done for ${channel.name}: ${channel.id}`); } catch (err) { - console.log(`Failed to fetchMessages of channle: ${channel.id} `, err) + console.log(`Failed to fetchMessages of channle: ${channel.id} `, err); } - } /** @@ -184,7 +181,7 @@ async function fetchMessages( */ export default async function fetchChannelMessages(connection: Connection, channel: TextChannel, period: Date) { try { - console.log(`fetch channel messages is running for ${channel.name}: ${channel.id} : ${channel.type}`) + console.log(`fetch channel messages is running for ${channel.name}: ${channel.id} : ${channel.type}`); const oldestChannelRawInfo = await rawInfoService.getOldestRawInfo(connection, { channelId: channel?.id, threadId: null, @@ -228,9 +225,9 @@ export default async function fetchChannelMessages(connection: Connection, chann await fetchMessages(connection, thread, undefined, period, 'before'); } } - console.log(`fetch channel messages is done for ${channel.name}: ${channel.id} : ${channel.type}`) - console.log('###################################') + console.log(`fetch channel messages is done for ${channel.name}: ${channel.id} : ${channel.type}`); + console.log('###################################'); } catch (err) { - console.log(`Failed to fetchChannelMessages of channle: ${channel.id} `, err) + console.log(`Failed to fetchChannelMessages of channle: ${channel.id} `, err); } } diff --git a/src/functions/fetchRoles.ts b/src/functions/fetchRoles.ts index e11af3f3..d48b0f03 100644 --- a/src/functions/fetchRoles.ts +++ b/src/functions/fetchRoles.ts @@ -4,51 +4,50 @@ import { IRole } from '@togethercrew.dev/db'; import { roleService, guildService } from '../database/services'; /** -* Extracts necessary data from a given role. -* @param {Role} role - The discord.js role object from which data is to be extracted. -* @returns {IRole} - The extracted data in the form of an IRole object. -*/ + * Extracts necessary data from a given role. + * @param {Role} role - The discord.js role object from which data is to be extracted. + * @returns {IRole} - The extracted data in the form of an IRole object. + */ function getNeedDataFromRole(role: Role): IRole { - return { - roleId: role.id, - name: role.name, - color: role.color, - }; + return { + roleId: role.id, + name: role.name, + color: role.color, + }; } /** -* Iterates over a list of roles and pushes extracted data from each role to an array. -* @param {IRole[]} arr - The array to which extracted data will be pushed. -* @param {Role[]} roleArray - An array of roles from which data is to be extracted. -* @returns {IRole[]} - The updated array containing the extracted data. -*/ + * Iterates over a list of roles and pushes extracted data from each role to an array. + * @param {IRole[]} arr - The array to which extracted data will be pushed. + * @param {Role[]} roleArray - An array of roles from which data is to be extracted. + * @returns {IRole[]} - The updated array containing the extracted data. + */ function pushRolesToArray(arr: IRole[], roleArray: Role[]): IRole[] { - for (const role of roleArray) { - arr.push(getNeedDataFromRole(role)); - } - return arr; + for (const role of roleArray) { + arr.push(getNeedDataFromRole(role)); + } + return arr; } - /** -* Fetches and saves role information from a given guild. -* @param {Connection} connection - Mongoose connection object for the database. -* @param {Client} client - The discord.js client object used to fetch the guild. -* @param {Snowflake} guildId - The identifier of the guild to extract roles from. -*/ + * Fetches and saves role information from a given guild. + * @param {Connection} connection - Mongoose connection object for the database. + * @param {Client} client - The discord.js client object used to fetch the guild. + * @param {Snowflake} guildId - The identifier of the guild to extract roles from. + */ export default async function fetchGuildRoles(connection: Connection, client: Client, guildId: Snowflake) { - console.log(`Fetching roles for guild: ${guildId}`) - try { - if (!client.guilds.cache.has(guildId)) { - await guildService.updateGuild({ guildId }, { isDisconnected: false }) - return - } - const guild = await client.guilds.fetch(guildId); - const rolesToStore: IRole[] = []; - pushRolesToArray(rolesToStore, [...guild.roles.cache.values()]); - await roleService.createRoles(connection, rolesToStore); // assuming a 'roleService' - } catch (err) { - console.error(`Failed to fetch roles of guild ${guildId}`, err); + console.log(`Fetching roles for guild: ${guildId}`); + try { + if (!client.guilds.cache.has(guildId)) { + await guildService.updateGuild({ guildId }, { isDisconnected: false }); + return; } - console.log(`Completed fetching roles for guild: ${guildId}`) -} \ No newline at end of file + const guild = await client.guilds.fetch(guildId); + const rolesToStore: IRole[] = []; + pushRolesToArray(rolesToStore, [...guild.roles.cache.values()]); + await roleService.createRoles(connection, rolesToStore); // assuming a 'roleService' + } catch (err) { + console.error(`Failed to fetch roles of guild ${guildId}`, err); + } + console.log(`Completed fetching roles for guild: ${guildId}`); +} diff --git a/src/functions/guildExtraction.ts b/src/functions/guildExtraction.ts index c01ec4e1..04962bb9 100644 --- a/src/functions/guildExtraction.ts +++ b/src/functions/guildExtraction.ts @@ -11,10 +11,10 @@ import { Connection } from 'mongoose'; */ export default async function guildExtraction(connection: Connection, client: Client, guildId: Snowflake) { try { - console.log(`********guild Extraction is running for ${(guildId)}********`) + console.log(`********guild Extraction is running for ${guildId}********`); if (!client.guilds.cache.has(guildId)) { - await guildService.updateGuild({ guildId }, { isDisconnected: false }) - return + await guildService.updateGuild({ guildId }, { isDisconnected: false }); + return; } const guild = await client.guilds.fetch(guildId); const guildDoc = await guildService.getGuild({ guildId }); @@ -29,14 +29,13 @@ export default async function guildExtraction(connection: Connection, client: Cl await fetchChannelMessages(connection, channel, guildDoc?.period); } } - console.log(`********guild Extraction is done for ${guildId}********`) - console.log('###################################') + console.log(`********guild Extraction is done for ${guildId}********`); + console.log('###################################'); } catch (err) { console.log(err); } } - // GUILD : 980858613587382322 // Channel name: Text Channels, ID: 980858613587382323 // Channel name: Voice Channels, ID: 980858613587382324 @@ -59,4 +58,4 @@ export default async function guildExtraction(connection: Connection, client: Cl // Channel name: c4, ID: 1109052576978173982 // Channel name: do-not-spam-here, ID: 1109369850276610048 // Channel name: do-not-spam, ID: 1109421233436635198 -// Channel name: test1, ID: 1110556724844310568 \ No newline at end of file +// Channel name: test1, ID: 1110556724844310568 diff --git a/src/functions/loadEvents.ts b/src/functions/loadEvents.ts index 757092b6..67164d8c 100644 --- a/src/functions/loadEvents.ts +++ b/src/functions/loadEvents.ts @@ -7,7 +7,9 @@ export default async function loadEvents(client: Client) { const eventFolders: string[] = await readdir(foldersPath); for (const folder of eventFolders) { const eventsPath: string = path.join(foldersPath, folder); - const eventFiles: string[] = (await readdir(eventsPath)).filter(file => (file.endsWith('.ts') || file.endsWith('.js'))); + const eventFiles: string[] = (await readdir(eventsPath)).filter( + file => file.endsWith('.ts') || file.endsWith('.js') + ); for (const file of eventFiles) { const filePath: string = path.join(eventsPath, file); const event = (await import(filePath)).default; diff --git a/src/functions/sendDirectMessage.ts b/src/functions/sendDirectMessage.ts index b55ddfe4..7e578a7e 100644 --- a/src/functions/sendDirectMessage.ts +++ b/src/functions/sendDirectMessage.ts @@ -1,19 +1,19 @@ -import { Client, Snowflake } from "discord.js"; +import { Client, Snowflake } from 'discord.js'; /** - * - * @param client - * @param info + * + * @param client + * @param info * @returns throw error if User has DMs closed or has no mutual servers with the bot */ -export default async function sendDirectMessage(client: Client, info: { discordId: Snowflake, message: string }) { - const { discordId, message } = info +export default async function sendDirectMessage(client: Client, info: { discordId: Snowflake; message: string }) { + const { discordId, message } = info; - // Fetch the user object - const user = await client.users.fetch(discordId) + // Fetch the user object + const user = await client.users.fetch(discordId); - // Send a private message - const sendResponse = await user.send(message); + // Send a private message + const sendResponse = await user.send(message); - return sendResponse + return sendResponse; } diff --git a/src/functions/thread.ts b/src/functions/thread.ts index 135aaeae..71dcc76d 100644 --- a/src/functions/thread.ts +++ b/src/functions/thread.ts @@ -1,22 +1,25 @@ -import { ChannelType, TextChannel } from "discord.js"; +import { ChannelType, TextChannel } from 'discord.js'; /** * create a private thread on specific channel and send a message to it * @param channel channel you want create a thread on it - * @param info + * @param info * @returns thread object */ -export async function createPrivateThreadAndSendMessage(channel: TextChannel, info: { threadName: string, message: string, threadReason?: string }) { - const { threadName, message, threadReason } = info +export async function createPrivateThreadAndSendMessage( + channel: TextChannel, + info: { threadName: string; message: string; threadReason?: string } +) { + const { threadName, message, threadReason } = info; - const thread = await channel.threads.create({ - name: threadName, - reason: threadReason, - type: ChannelType.PrivateThread, - invitable: false, - }); + const thread = await channel.threads.create({ + name: threadName, + reason: threadReason, + type: ChannelType.PrivateThread, + invitable: false, + }); - await thread.send(message) + await thread.send(message); - return thread + return thread; } diff --git a/src/index.ts b/src/index.ts index 13e22f94..5ea7d505 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,61 +32,75 @@ const client = new Client({ ], }); -const partial = (func: any, ...args: any) => (...rest: any) => func(...args, ...rest) +const partial = + (func: any, ...args: any) => + (...rest: any) => + func(...args, ...rest); + +const getSagaFromRabbitMessage = async (msg: Record) => { + if (!msg) return; + + const { content } = msg; + const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); + + return saga; +}; const fetchMethod = async (msg: any) => { - console.log(`Starting fetch initial with: ${msg}`) + console.log(`Starting fetch initial with: ${msg}`); if (!msg) return; - const { content } = msg - const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }) - const guildId = saga.data["guildId"]; - const isGuildCreated = saga.data["created"]; + const saga = await getSagaFromRabbitMessage(msg); + const guildId = saga.data['guildId']; + const isGuildCreated = saga.data['created']; const connection = await databaseService.connectionFactory(guildId, config.mongoose.dbURL); if (isGuildCreated) { - await fetchMembers(connection, client, guildId) + await fetchMembers(connection, client, guildId); + } else { + await guildExtraction(connection, client, guildId); } - else { - await guildExtraction(connection, client, guildId) - } - await closeConnection(connection) - console.log(`Finished fetch initial data.`) -} - -const notifyUserAboutAnalysisFinish = async (discordId: string, info: { guildId: Snowflake, message: string, useFallback: boolean }) => { + await closeConnection(connection); + console.log(`Finished fetch initial data.`); +}; + +const notifyUserAboutAnalysisFinish = async ( + discordId: string, + info: { guildId: Snowflake; message: string; useFallback: boolean } +) => { // related issue https://github.com/RnDAO/tc-discordBot/issues/68 const { guildId, message, useFallback } = info; const guild = await client.guilds.fetch(guildId); - const channels = await guild.channels.fetch() + const channels = await guild.channels.fetch(); - const arrayChannels = Array.from(channels, ([name, value]) => ({ ...value } as Channel)) - const textChannels = arrayChannels.filter(channel => channel.type == ChannelType.GuildText) as TextChannel[] - const rawPositionBasedSortedTextChannels = textChannels.sort((textChannelA, textChannelB) => textChannelA.rawPosition > textChannelB.rawPosition ? 1 : -1) - const upperTextChannel = rawPositionBasedSortedTextChannels[0] + const arrayChannels = Array.from(channels, ([name, value]) => ({ ...value } as Channel)); + const textChannels = arrayChannels.filter(channel => channel.type == ChannelType.GuildText) as TextChannel[]; + const rawPositionBasedSortedTextChannels = textChannels.sort((textChannelA, textChannelB) => + textChannelA.rawPosition > textChannelB.rawPosition ? 1 : -1 + ); + const upperTextChannel = rawPositionBasedSortedTextChannels[0]; try { - sendDirectMessage(client, { discordId, message }) + sendDirectMessage(client, { discordId, message }); } catch (error) { - - // can not send DM to the user + // can not send DM to the user // Will create a private thread and notify him/her about the status if useFallback is true if (useFallback) - createPrivateThreadAndSendMessage(upperTextChannel, - { threadName: 'TogetherCrew Status', message: `<@${discordId}> ${message}` } - ) - + createPrivateThreadAndSendMessage(upperTextChannel, { + threadName: 'TogetherCrew Status', + message: `<@${discordId}> ${message}`, + }); } -} +}; const fetchInitialData = async (guildId: Snowflake) => { const connection = await databaseService.connectionFactory(guildId, config.mongoose.dbURL); - await fetchRoles(connection, client, guildId) - await fetchChannels(connection, client, guildId) - await fetchMembers(connection, client, guildId) - await closeConnection(connection) -} + await fetchRoles(connection, client, guildId); + await fetchChannels(connection, client, guildId); + await fetchMembers(connection, client, guildId); + await closeConnection(connection); +}; // APP async function app() { @@ -94,56 +108,50 @@ async function app() { await client.login(config.discord.botToken); await connectDB(); - // *****************************RABBITMQ - await MBConnection.connect(config.mongoose.dbURL) + await MBConnection.connect(config.mongoose.dbURL); await RabbitMQ.connect(config.rabbitMQ.url, RabbitMQQueue.DISCORD_BOT).then(() => { - console.log("Connected to RabbitMQ!") - }) - RabbitMQ.onEvent(Event.DISCORD_BOT.FETCH, async (msg) => { - console.log(`Received ${Event.DISCORD_BOT.FETCH} event with msg: ${msg}`) - if (!msg) return - - const { content } = msg - const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }) - - const fn = partial(fetchMethod, msg) - await saga.next(fn) - console.log(`Finished ${Event.DISCORD_BOT.FETCH} event with msg: ${msg}`) - }) + console.log('Connected to RabbitMQ!'); + }); + RabbitMQ.onEvent(Event.DISCORD_BOT.FETCH, async msg => { + console.log(`Received ${Event.DISCORD_BOT.FETCH} event with msg: ${msg}`); + if (!msg) return; - RabbitMQ.onEvent(Event.DISCORD_BOT.SEND_MESSAGE, async (msg) => { - console.log(`Received ${Event.DISCORD_BOT.SEND_MESSAGE} event with msg: ${msg}`) - if (!msg) return + const saga = await getSagaFromRabbitMessage(msg); - const { content } = msg - const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }) + const fn = partial(fetchMethod, msg); + await saga.next(fn); + console.log(`Finished ${Event.DISCORD_BOT.FETCH} event with msg: ${msg}`); + }); - const guildId = saga.data["guildId"]; - const discordId = saga.data["discordId"]; - const message = saga.data["message"]; - const useFallback = saga.data["useFallback"]; + RabbitMQ.onEvent(Event.DISCORD_BOT.SEND_MESSAGE, async msg => { + console.log(`Received ${Event.DISCORD_BOT.SEND_MESSAGE} event with msg: ${msg}`); + if (!msg) return; - const fn = notifyUserAboutAnalysisFinish.bind({}, discordId, { guildId, message, useFallback }) - await saga.next(fn) - console.log(`Finished ${Event.DISCORD_BOT.SEND_MESSAGE} event with msg: ${msg}`) + const saga = await getSagaFromRabbitMessage(msg); - }) + const guildId = saga.data['guildId']; + const discordId = saga.data['discordId']; + const message = saga.data['message']; + const useFallback = saga.data['useFallback']; - RabbitMQ.onEvent(Event.DISCORD_BOT.FETCH_MEMBERS, async (msg) => { - console.log(`Received ${Event.DISCORD_BOT.FETCH_MEMBERS} event with msg: ${msg}`) - if (!msg) return + const fn = notifyUserAboutAnalysisFinish.bind({}, discordId, { guildId, message, useFallback }); + await saga.next(fn); + console.log(`Finished ${Event.DISCORD_BOT.SEND_MESSAGE} event with msg: ${msg}`); + }); - const { content } = msg - const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }) + RabbitMQ.onEvent(Event.DISCORD_BOT.FETCH_MEMBERS, async msg => { + console.log(`Received ${Event.DISCORD_BOT.FETCH_MEMBERS} event with msg: ${msg}`); + if (!msg) return; - const guildId = saga.data["guildId"]; + const saga = await getSagaFromRabbitMessage(msg); - const fn = fetchInitialData.bind({}, guildId) - await saga.next(fn) - console.log(`Finished ${Event.DISCORD_BOT.FETCH_MEMBERS} event with msg: ${msg}`) - }) + const guildId = saga.data['guildId']; + const fn = fetchInitialData.bind({}, guildId); + await saga.next(fn); + console.log(`Finished ${Event.DISCORD_BOT.FETCH_MEMBERS} event with msg: ${msg}`); + }); // *****************************BULLMQ // Create a queue instance with the Redis connection @@ -151,8 +159,8 @@ async function app() { connection: { host: config.redis.host, port: config.redis.port, - password: config.redis.password - } + password: config.redis.password, + }, }); queue.add('cronJob', {}, { repeat: { @@ -170,18 +178,22 @@ async function app() { // Create a worker to process the job // eslint-disable-next-line @typescript-eslint/no-explicit-any - const worker = new Worker('cronJobQueue', async (job: Job | undefined) => { - if (job) { - // Call the extractMessagesDaily function - await cronJob(client); - } - }, { - connection: { - host: config.redis.host, - port: config.redis.port, - password: config.redis.password + const worker = new Worker( + 'cronJobQueue', + async (job: Job | undefined) => { + if (job) { + // Call the extractMessagesDaily function + await cronJob(client); + } + }, + { + connection: { + host: config.redis.host, + port: config.redis.port, + password: config.redis.password, + }, } - }); + ); // Listen for completed and failed events to log the job status worker.on('completed', job => { diff --git a/tsconfig.json b/tsconfig.json index 3b968e27..9f475db6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,33 +1,25 @@ { - "compilerOptions": { - "module": "commonjs", - "declaration": false, - "removeComments": false, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "target": "ES2017", - "sourceMap": true, - "outDir": "./lib", - "baseUrl": "./", - "incremental": true, - "strict": true, - "noImplicitAny": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true - }, - "include": [ - "src/**/*", - "__tests__/**/*" - ], - "ts-node": { - "transpileOnly": true, - "files": true - }, - "exclude": [ - "./coverage", - "./lib", - "__tests__", - "jest.config.js" - ], -} \ No newline at end of file + "compilerOptions": { + "module": "commonjs", + "declaration": false, + "removeComments": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "ES2017", + "sourceMap": true, + "outDir": "./lib", + "baseUrl": "./", + "incremental": true, + "strict": true, + "noImplicitAny": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "include": ["src/**/*", "__tests__/**/*"], + "ts-node": { + "transpileOnly": true, + "files": true + }, + "exclude": ["./coverage", "./lib", "__tests__", "jest.config.js"] +}