diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..0592f618 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.github/ +coverage/ +dist/ +node_modules/ + +.env \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index e804067b..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,22 +0,0 @@ -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 diff --git a/.eslintrc.json b/.eslintrc.json index 92fa887e..ab0df901 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,24 +1,27 @@ { - "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" - } + "env": { + "es2021": true, + "node": true + }, + "extends": [ + "standard-with-typescript", + "prettier" + ], + "overrides": [], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "project": "./tsconfig.json" + }, + "rules": {}, + "ignorePatterns": [ + "coverage", + "dist", + "__tests__/", + "jest.config.js", + "*.yml", + "mongo-init.js", + "bable.config.js" + ] } \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17715391..eaddf546 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,41 +1,10 @@ -name: tc-discordBot +--- +name: CI 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 }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..1cccff3b --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,12 @@ +--- +name: Publish + +on: + release: + types: [published] + +jobs: + ci: + uses: TogetherCrew/operations/.github/workflows/publish.ts.yml@main + secrets: + NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..e69de29b diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 5123b271..00000000 --- a/.prettierrc +++ /dev/null @@ -1,22 +0,0 @@ -{ - "printWidth": 120, - "singleQuote": true, - "trailingComma": "es5", - "arrowParens": "avoid", - "endOfLine": "auto", - "overrides": [ - { - "files": ".json", - "options": { - "singleQuote": false - } - }, - { - "files": ".rc", - "options": { - "singleQuote": false, - "parser": "json" - } - } - ] -} diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..a0d1c9a9 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "printWidth": 120, + "trailingComma": "all", + "singleQuote": true +} diff --git a/README.md b/README.md index 327ac98a..22e3e06c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ [![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 +# tc-discordBot diff --git a/__tests__/fixtures/channel.fixture.ts b/__tests__/fixtures/channel.fixture.ts index 1246412a..fa489340 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/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..07dcf98b 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..20901518 100644 --- a/__tests__/unit/database/services/channel.test.ts +++ b/__tests__/unit/database/services/channel.test.ts @@ -1,198 +1,160 @@ import mongoose, { Connection } from 'mongoose'; -import { - IChannel, - channelSchema, - IChannelUpdateBody, -} from '@togethercrew.dev/db'; +import { IChannel, channelSchema, IChannelUpdateBody, DatabaseManager } 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 DatabaseManager.getInstance().getTenantDb('connection-1'); + }); + afterAll(async () => { + await connection.close(); + }); + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + + describe('createChannel', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + + 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 }); - beforeAll(async () => { - connection = await mongoose.createConnection(config.mongoose.serverURL, { - dbName: 'connection', - }); - connection.model('Channel', channelSchema); + expect(channelDoc1).toBeDefined(); + expect(channelDoc1).toMatchObject({ + channelId: channel1.channelId, + name: channel1.name, + parentId: channel1.parentId, + }); }); + }); - afterAll(async () => { - await connection.close(); + describe('createChannels', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + 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('getChannel', () => { 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, - - }); - }); - }); - - 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('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]); - }); - - 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('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); - }); + await connection.collection('channels').deleteMany({}); + }); + 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', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + 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([]); + }); + }); + + describe('updateChannel', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + 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('updateChannels', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + 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/guildMember.test.ts b/__tests__/unit/database/services/guildMember.test.ts index 4692ecd8..40cdd728 100644 --- a/__tests__/unit/database/services/guildMember.test.ts +++ b/__tests__/unit/database/services/guildMember.test.ts @@ -1,50 +1,34 @@ import mongoose, { Connection } from 'mongoose'; -import { - IGuildMember, - guildMemberSchema, - IGuildMemberUpdateBody, -} from '@togethercrew.dev/db'; +import { IGuildMember, guildMemberSchema, IGuildMemberUpdateBody, DatabaseManager, GuildMember } 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(); describe('guildMember service', () => { let connection: Connection; - beforeAll(async () => { - connection = await mongoose.createConnection(config.mongoose.serverURL, { - dbName: 'connection', - }); - connection.model('GuildMember', guildMemberSchema); + connection = await DatabaseManager.getInstance().getTenantDb('connection-2'); }); - afterAll(async () => { await connection.close(); }); - beforeEach(async () => { - await connection.db.dropDatabase(); + await connection.collection('guildmembers').deleteMany({}); }); - describe('createGuidMember', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); 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({ @@ -55,22 +39,20 @@ describe('guildMember service', () => { }); describe('createGuidMembers', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); 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({ @@ -86,6 +68,9 @@ describe('guildMember service', () => { }); describe('getGuildMember', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); test('should retrieve an existing guild member that match the filter criteria', async () => { await guildMemberService.createGuildMember(connection, guildMember1); const result = await guildMemberService.getGuildMember(connection, { @@ -103,12 +88,11 @@ describe('guildMember service', () => { }); describe('getGuildMembers', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); 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 +100,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'], }); @@ -129,6 +109,9 @@ describe('guildMember service', () => { }); describe('updateGuildMember', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); const updateBody: IGuildMemberUpdateBody = { username: 'userName', avatar: 'new-avatar.png', @@ -139,14 +122,15 @@ describe('guildMember service', () => { const result = await guildMemberService.updateGuildMember( connection, { discordId: guildMember1.discordId }, - updateBody + updateBody, ); + + 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({ @@ -159,52 +143,44 @@ describe('guildMember service', () => { test('should return null when no guild member match the filter criteria', async () => { const result = await guildMemberService.updateGuildMember( connection, - { discordId: guildMember1.discordId }, - updateBody + { discordId: '1' }, + updateBody, ); expect(result).toEqual(null); }); }); describe('updateGuildMembers', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); const updateBody: IGuildMemberUpdateBody = { 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); }); }); describe('deleteGuildMember', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); test('should delete guild member that match the filter criteria', async () => { await guildMemberService.createGuildMember(connection, guildMember1); const result = await guildMemberService.deleteGuildMember(connection, { @@ -222,12 +198,11 @@ describe('guildMember service', () => { }); describe('deleteGuildMembers', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); 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 +216,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..d4dde597 100644 --- a/__tests__/unit/database/services/rawInfo.test.ts +++ b/__tests__/unit/database/services/rawInfo.test.ts @@ -1,35 +1,26 @@ import mongoose, { Connection } from 'mongoose'; -import { IRawInfo, IRawInfoUpdateBody, rawInfoSchema } from '@togethercrew.dev/db'; +import { IRawInfoUpdateBody, DatabaseManager } 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'; setupTestDB(); describe('rawInfo service', () => { let connection: Connection; - beforeAll(async () => { - connection = await mongoose.createConnection(config.mongoose.serverURL, { - dbName: 'connection', - }); - connection.model('RawInfo', rawInfoSchema); + connection = await DatabaseManager.getInstance().getTenantDb('connection-3'); }); - afterAll(async () => { await connection.close(); }); - beforeEach(async () => { - await connection.db.dropDatabase(); + await connection.collection('rawinfos').deleteMany({}); }); - describe('createRawInfo', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); test('should create a rawInfo', async () => { const result = await rawInfoService.createRawInfo(connection, rawInfo1); expect(result).toBeDefined(); @@ -48,12 +39,11 @@ describe('rawInfo service', () => { }); describe('createRawInfos', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); 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, { @@ -83,6 +73,9 @@ describe('rawInfo service', () => { }); describe('getRawInfo', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); test('should retrieve an existing rawInfo that matches the filter criteria', async () => { await rawInfoService.createRawInfo(connection, rawInfo3); const result = await rawInfoService.getRawInfo(connection, { @@ -101,12 +94,11 @@ describe('rawInfo service', () => { }); describe('getRawInfos', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); 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 +107,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'], }); @@ -128,6 +116,9 @@ describe('rawInfo service', () => { }); describe('updateRawInfo', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); const updateBody: IRawInfoUpdateBody = { channelId: 'channel1', threadId: 'thread456', @@ -136,11 +127,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,16 +143,15 @@ 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(); }); }); describe('updateRawInfos', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); const updateBody: IRawInfoUpdateBody = { channelId: 'channel1', threadId: 'thread100000', @@ -176,18 +162,12 @@ describe('rawInfo service', () => { const result = await rawInfoService.updateManyRawInfo( connection, { role_mentions: rawInfo1.role_mentions }, - updateBody + updateBody, ); 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,16 +180,15 @@ 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); }); }); describe('deleteRawInfo', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); test('should delete rawInfo that matches the filter criteria', async () => { await rawInfoService.createRawInfo(connection, rawInfo1); const result = await rawInfoService.deleteRawInfo(connection, { @@ -227,12 +206,11 @@ describe('rawInfo service', () => { }); describe('deleteRawInfos', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); 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..46811d38 100644 --- a/__tests__/unit/database/services/role.test.ts +++ b/__tests__/unit/database/services/role.test.ts @@ -1,197 +1,158 @@ import mongoose, { Connection } from 'mongoose'; -import { - IRole, - roleSchema, - IRoleUpdateBody, -} from '@togethercrew.dev/db'; +import { IRole, roleSchema, IRoleUpdateBody, DatabaseManager } 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 DatabaseManager.getInstance().getTenantDb('connection-4'); + }); + afterAll(async () => { + await connection.close(); + }); + beforeEach(async () => { + await connection.collection('roles').deleteMany({}); + }); + describe('createRole', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + 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('createRoles', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + 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('getRole', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + 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); + }); + }); - beforeAll(async () => { - connection = await mongoose.createConnection(config.mongoose.serverURL, { - dbName: 'connection', - }); - connection.model('Role', roleSchema); + describe('getRoles', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + 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([]); + }); + }); + + describe('updateRole', () => { + beforeEach(async () => { + await connection.collection('channels').deleteMany({}); + }); + 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, + }); }); - afterAll(async () => { - await connection.close(); + 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', () => { 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, - }); - }); - }); - - 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('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]); - }); - - 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('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); - }); - }); - -}); \ No newline at end of file + await connection.collection('channels').deleteMany({}); + }); + 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); + }); + }); +}); diff --git a/__tests__/utils/setupTestDB.ts b/__tests__/utils/setupTestDB.ts index f67c6463..8932fae9 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 RabbitMQ, { MBConnection, Queue } from '@togethercrew.dev/tc-messagebroker'; 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({}))); - }); + }); - afterAll(async () => { - await mongoose.disconnect(); - }); + beforeEach(async () => { + await Promise.all(Object.values(mongoose.connection.collections).map(async (collection) => collection.deleteMany({}))); + }); + + afterAll(async () => { + await mongoose.disconnect(); + }); }; export default setupTestDB; \ No newline at end of file diff --git a/bable.config.js b/bable.config.js index b58143a8..a2efd0c1 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.test.yml b/docker-compose.test.yml index f56259d3..8069509f 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -7,31 +7,39 @@ services: target: test 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 + - NODE_ENV=test + - DB_HOST=mongo + - DB_PORT=27017 + - DB_USER=root + - DB_PASSWORD=pass + - DB_NAME=RnDAO + - DISCORD_CLIENT_ID=1 + - DISCORD_CLIENT_SECRET=1 + - SENTRY_DSN=1 + - SENTRY_ENV=test + - DISCORD_BOT_TOKEN=1 + - REDIS_HOST=redis + - REDIS_PORT=6379 + - REDIS_PASSWORD= + - RABBIT_HOST=localhost + - RABBIT_PORT=5672 + - RABBIT_USER=root + - RABBIT_PASSWORD=pass + - LOG_LEVEL=info volumes: - ./coverage:/project/coverage depends_on: - - redis - - mongo - redis: - image: "redis:alpine" + mongo: + condition: service_healthy mongo: - image: "mongo" + image: "mongo:5.0.10" environment: + - MONGO_INITDB_DATABASE=RnDAO - MONGO_INITDB_ROOT_USERNAME=root - MONGO_INITDB_ROOT_PASSWORD=pass - rabbitmq: - image: "rabbitmq:3-management-alpine" - environment: - - RABBITMQ_DEFAULT_USER=root - - RABBITMQ_DEFAULT_PASS=pass \ No newline at end of file + healthcheck: + test: echo 'db.stats().ok' | mongosh localhost:27017/test --quiet + interval: 60s + timeout: 10s + retries: 2 + start_period: 40s diff --git a/jest.config.js b/jest.config.js index e34f9740..0afca8d5 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", + 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, }, - 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/mongo-init.js b/mongo-init.js new file mode 100644 index 00000000..5baf5a68 --- /dev/null +++ b/mongo-init.js @@ -0,0 +1,13 @@ +db.createUser( + { + user: "user", + pwd: "123456Pass", + roles: [ + { + role: "readWrite", + db: "RnDAO" + } + ] + } +); +db.createCollection("test"); //MongoDB creates the database when you first store data in that database \ No newline at end of file diff --git a/package.json b/package.json index 4aa75c38..d11f8c18 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "test:ci": "jest --ci --detectOpenHandles", "lint": "eslint **/*.ts", "lint-fix": "eslint --fix **/*.ts", - "format": "prettier --write src/**/*.ts", + "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\" \"types/*.ts\" \"__tests__/**/*.ts\" \"*.js\" \"*.json\" \"*.md\"", "migrate:create": "migrate create --template-file ./src/migrations/utils/template.ts --migrations-dir=\"./src/migrations/db\"", "migrate:up": "migrate --migrations-dir=\"./lib/migrations/db\" up", "migrate:down": "migrate --migrations-dir=\"./lib/migrations/db\" down" diff --git a/src/commands/info/question.ts b/src/commands/info/question.ts index 448574bb..c9babfd3 100644 --- a/src/commands/info/question.ts +++ b/src/commands/info/question.ts @@ -1,35 +1,41 @@ +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { SlashCommandBuilder } from 'discord.js'; -import { interactionService } from '../../services' +import { interactionService } from '../../services'; import RabbitMQ, { Event, Queue as RabbitMQQueue } from '@togethercrew.dev/tc-messagebroker'; -import { ChatInputCommandInteraction_broker } from '../../interfaces/Hivemind.interface'; +import { type ChatInputCommandInteraction_broker } from '../../interfaces/Hivemind.interface'; import { handleBigInts, removeCircularReferences } from '../../utils/obj'; import logger from '../../config/logger'; export default { - data: new SlashCommandBuilder() - .setName('question') - .setDescription('Ask a question and get an answer from Hivemind!') - .addStringOption(option => - option.setName('question') - .setDescription('Your question to Hivemind') - .setRequired(true)), + data: new SlashCommandBuilder() + .setName('question') + .setDescription('Ask a question and get an answer from Hivemind!') + .addStringOption((option) => + option.setName('question').setDescription('Your question to Hivemind').setRequired(true), + ), - async execute(interaction: ChatInputCommandInteraction_broker) { - try { - // TogetherCrew-Leads: 983364577096003604 TogetherCrew-Contributors: 983364691692748832 - if (!(interaction.member?.roles.cache.has("983364577096003604") || interaction.member?.roles.cache.has("983364691692748832"))) { - return await interactionService.createInteractionResponse(interaction, { type: 4, data: { content: 'You do not have the required role to use this command!', flags: 64 } }) - } - const serializedInteraction = interactionService.constructSerializableInteraction(interaction); - const processedInteraction = handleBigInts(serializedInteraction); - const cleanInteraction = removeCircularReferences(processedInteraction); // Pass processedInteraction here - const serializedData = JSON.stringify(cleanInteraction, null, 2); - RabbitMQ.publish(RabbitMQQueue.HIVEMIND, Event.HIVEMIND.INTERACTION_CREATED, { interaction: serializedData }); - await interactionService.createInteractionResponse(interaction, { type: 5, data: { flags: 64 } }) - } catch (error) { - logger.error({ command: 'question', error }, 'is failed'); - - } - - }, -}; \ No newline at end of file + async execute(interaction: ChatInputCommandInteraction_broker) { + try { + // TogetherCrew-Leads: 983364577096003604 TogetherCrew-Contributors: 983364691692748832 + if ( + !( + interaction.member?.roles.cache.has('983364577096003604') || + interaction.member?.roles.cache.has('983364691692748832') + ) + ) { + return await interactionService.createInteractionResponse(interaction, { + type: 4, + data: { content: 'You do not have the required role to use this command!', flags: 64 }, + }); + } + const serializedInteraction = interactionService.constructSerializableInteraction(interaction); + const processedInteraction = handleBigInts(serializedInteraction); + const cleanInteraction = removeCircularReferences(processedInteraction); // Pass processedInteraction here + const serializedData = JSON.stringify(cleanInteraction, null, 2); + RabbitMQ.publish(RabbitMQQueue.HIVEMIND, Event.HIVEMIND.INTERACTION_CREATED, { interaction: serializedData }); + await interactionService.createInteractionResponse(interaction, { type: 5, data: { flags: 64 } }); + } catch (error) { + logger.error({ command: 'question', error }, 'is failed'); + } + }, +}; diff --git a/src/config/index.ts b/src/config/index.ts index ace3287e..deaf7299 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ import Joi from 'joi'; const envVarsSchema = Joi.object() @@ -26,14 +27,14 @@ const envVarsSchema = Joi.object() const { value: envVars, error } = envVarsSchema.prefs({ errors: { label: 'key' } }).validate(process.env); -if (error) { +if (error != null) { throw new Error(`Config validation error: ${error.message}`); } 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}`, + serverURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}/${envVars.DB_NAME}?authSource=admin`, + dbURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}?authSource=admin`, }, redis: { host: envVars.REDIS_HOST, diff --git a/src/config/logger.ts b/src/config/logger.ts index cabd9b48..f0557d94 100644 --- a/src/config/logger.ts +++ b/src/config/logger.ts @@ -4,7 +4,7 @@ import config from './index'; export default pino({ level: config.logger.level, formatters: { - level: label => { + level: (label) => { return { level: label.toUpperCase() }; }, }, diff --git a/src/config/queue.ts b/src/config/queue.ts index 6f0efbf2..5a022d89 100644 --- a/src/config/queue.ts +++ b/src/config/queue.ts @@ -1,22 +1,22 @@ import config from './index'; export const redisConfig = { - host: config.redis.host, - port: config.redis.port, - password: config.redis.password, + host: config.redis.host, + port: config.redis.port, + password: config.redis.password, }; export const cronJobConfig = { - cron: '0 0 * * *', // Run once at 00:00 UTC - jobId: 'cronJob', - attempts: 0, - backoff: { - type: 'exponential', - delay: 1000, - }, + cron: '0 0 * * *', // Run once at 00:00 UTC + jobId: 'cronJob', + attempts: 0, + backoff: { + type: 'exponential', + delay: 1000, + }, }; export const rateLimitConfig = { - max: 20, - duration: 10000, -}; \ No newline at end of file + max: 20, + duration: 10000, +}; diff --git a/src/database/connection.ts b/src/database/connection.ts index 039970bd..1e1de09c 100644 --- a/src/database/connection.ts +++ b/src/database/connection.ts @@ -3,12 +3,12 @@ import config from '../config'; import { MBConnection } from '@togethercrew.dev/tc-messagebroker'; import logger from '../config/logger'; -mongoose.set("strictQuery", true); +mongoose.set('strictQuery', true); // Connect to Message Broker DB -export const connectToMB = async () => { +export const connectToMB = async (): Promise => { try { - await MBConnection.connect(config.mongoose.dbURL); + MBConnection.connect(config.mongoose.dbURL); logger.info({ url: config.mongoose.dbURL }, 'Setuped Message Broker connection!'); } catch (error) { logger.fatal({ url: config.mongoose.dbURL, error }, 'Failed to setup to Message Broker!!'); @@ -16,11 +16,11 @@ export const connectToMB = async () => { }; // Connect to MongoDB -export const connectToMongoDB = async () => { +export const connectToMongoDB = async (): Promise => { try { await mongoose.connect(config.mongoose.serverURL); logger.info({ url: config.mongoose.serverURL }, 'Connected to MongoDB!'); } catch (error) { - logger.fatal({ url: config.mongoose.serverURL, error }, 'Failed to connect to MongoDB!') + logger.fatal({ url: config.mongoose.serverURL, error }, 'Failed to connect to MongoDB!'); } -}; \ No newline at end of file +}; diff --git a/src/database/services/channel.service.ts b/src/database/services/channel.service.ts index 9d401bbb..a5725399 100644 --- a/src/database/services/channel.service.ts +++ b/src/database/services/channel.service.ts @@ -1,6 +1,7 @@ -import { Connection } from 'mongoose'; -import { IChannel, IChannelMethods, IChannelUpdateBody } from '@togethercrew.dev/db'; -import { VoiceChannel, TextChannel, CategoryChannel } from 'discord.js'; +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ +import { type Connection } from 'mongoose'; +import { type IChannel, type IChannelMethods, type IChannelUpdateBody } from '@togethercrew.dev/db'; +import { type VoiceChannel, type TextChannel, type CategoryChannel } from 'discord.js'; import parentLogger from '../../config/logger'; const logger = parentLogger.child({ module: 'ChannelService' }); @@ -16,7 +17,7 @@ async function createChannel(connection: Connection, channel: IChannel): Promise return await connection.models.Channel.create(channel); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - if (error.code == 11000) { + if (error.code === 11000) { logger.warn({ database: connection.name, channel_id: channel.channelId }, 'Failed to create duplicate channel'); return null; } @@ -36,7 +37,7 @@ async function createChannels(connection: Connection, channels: IChannel[]): Pro return await connection.models.Channel.insertMany(channels, { ordered: false }); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - if (error.code == 11000) { + if (error.code === 11000) { logger.warn({ database: connection.name }, 'Failed to create duplicate channels'); return []; } @@ -75,15 +76,16 @@ async function getChannels(connection: Connection, filter: object): Promise { try { const channel = await connection.models.Channel.findOne(filter); - if (!channel) { + if (channel === null) { return null; } Object.assign(channel, updateBody); - return await channel.save(); + await channel.save(); + return channel; } catch (error) { logger.error({ database: connection.name, filter, updateBody, error }, 'Failed to update channel'); return null; @@ -100,6 +102,7 @@ async function updateChannel( async function updateChannels(connection: Connection, filter: object, updateBody: IChannelUpdateBody): Promise { try { const updateResult = await connection.models.Channel.updateMany(filter, updateBody); + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions return updateResult.modifiedCount || 0; } catch (error) { logger.error({ database: connection.name, filter, updateBody, error }, 'Failed to update channels'); @@ -116,7 +119,7 @@ async function updateChannels(connection: Connection, filter: object, updateBody */ async function handelChannelChanges( connection: Connection, - channel: TextChannel | VoiceChannel | CategoryChannel + channel: TextChannel | VoiceChannel | CategoryChannel, ): Promise { const commonFields = getNeededDateFromChannel(channel); try { @@ -139,7 +142,7 @@ function getNeededDateFromChannel(channel: TextChannel | VoiceChannel | Category channelId: channel.id, name: channel.name, // cast to TextChannel for 'name' parentId: channel.parentId, - permissionOverwrites: Array.from(channel.permissionOverwrites.cache.values()).map(overwrite => ({ + permissionOverwrites: Array.from(channel.permissionOverwrites.cache.values()).map((overwrite) => ({ id: overwrite.id, type: overwrite.type, allow: overwrite.allow.bitfield.toString(), diff --git a/src/database/services/guildMember.service.ts b/src/database/services/guildMember.service.ts index dbbb2d85..e2554fd6 100644 --- a/src/database/services/guildMember.service.ts +++ b/src/database/services/guildMember.service.ts @@ -1,6 +1,6 @@ -import { Connection } from 'mongoose'; -import { IGuildMember, IGuildMemberMethods, IGuildMemberUpdateBody } from '@togethercrew.dev/db'; -import { GuildMember } from 'discord.js'; +import { type Connection } from 'mongoose'; +import { type IGuildMember, type IGuildMemberMethods, type IGuildMemberUpdateBody } from '@togethercrew.dev/db'; +import { type GuildMember } from 'discord.js'; import parentLogger from '../../config/logger'; const logger = parentLogger.child({ module: 'GuildMemberService' }); @@ -15,16 +15,16 @@ async function createGuildMember(connection: Connection, guildMember: IGuildMemb return await connection.models.GuildMember.create(guildMember); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - if (error.code == 11000) { + if (error.code === 11000) { logger.warn( { database: connection.name, guild_member_id: guildMember.discordId }, - 'Failed to create duplicate guild member' + 'Failed to create duplicate guild member', ); return null; } logger.error( { database: connection.name, guild_member_id: guildMember.discordId, error }, - 'Failed to create guild member' + 'Failed to create guild member', ); return null; } @@ -41,7 +41,7 @@ async function createGuildMembers(connection: Connection, guildMembers: IGuildMe return await connection.models.GuildMember.insertMany(guildMembers, { ordered: false }); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - if (error.code == 11000) { + if (error.code === 11000) { logger.warn({ database: connection.name }, 'Failed to create duplicate guild members'); return []; } @@ -58,7 +58,7 @@ async function createGuildMembers(connection: Connection, guildMembers: IGuildMe */ async function getGuildMember( connection: Connection, - filter: object + filter: object, ): Promise<(IGuildMember & IGuildMemberMethods) | null> { return await connection.models.GuildMember.findOne(filter); } @@ -83,15 +83,16 @@ async function getGuildMembers(connection: Connection, filter: object): Promise< async function updateGuildMember( connection: Connection, filter: object, - updateBody: IGuildMemberUpdateBody + updateBody: IGuildMemberUpdateBody, ): Promise { try { const guildMember = await connection.models.GuildMember.findOne(filter); - if (!guildMember) { + if (guildMember === null) { return null; } Object.assign(guildMember, updateBody); - return await guildMember.save(); + await guildMember.save(); + return guildMember; } catch (error) { logger.error({ database: connection.name, filter, updateBody, error }, 'Failed to update guild member'); return null; @@ -108,10 +109,11 @@ async function updateGuildMember( async function updateGuildMembers( connection: Connection, filter: object, - updateBody: IGuildMemberUpdateBody + updateBody: IGuildMemberUpdateBody, ): Promise { try { const updateResult = await connection.models.GuildMember.updateMany(filter, updateBody); + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions return updateResult.modifiedCount || 0; } catch (error) { logger.error({ database: connection.name, filter, updateBody, error }, 'Failed to update guild members'); @@ -162,7 +164,7 @@ async function handelGuildMemberChanges(connection: Connection, guildMember: Gui const commonFields = getNeededDateFromGuildMember(guildMember); try { const guildMemberDoc = await updateGuildMember(connection, { discordId: guildMember.user.id }, commonFields); - if (!guildMemberDoc) { + if (guildMemberDoc === null) { await createGuildMember(connection, { ...commonFields, isBot: guildMember.user.bot, @@ -171,7 +173,7 @@ async function handelGuildMemberChanges(connection: Connection, guildMember: Gui } catch (error) { logger.error( { guild_id: connection.name, guild_member_id: guildMember.id, error }, - 'Failed to handle guild member changes' + 'Failed to handle guild member changes', ); } } @@ -187,7 +189,7 @@ function getNeededDateFromGuildMember(guildMember: GuildMember): IGuildMember { username: guildMember.user.username, avatar: guildMember.user.avatar, joinedAt: guildMember.joinedAt, - roles: guildMember.roles.cache.map(role => role.id), + roles: guildMember.roles.cache.map((role) => role.id), discriminator: guildMember.user.discriminator, permissions: guildMember.permissions.bitfield.toString(), nickname: guildMember.nickname, diff --git a/src/database/services/platform.service.ts b/src/database/services/platform.service.ts index 08588abc..03fd2c00 100644 --- a/src/database/services/platform.service.ts +++ b/src/database/services/platform.service.ts @@ -1,6 +1,6 @@ -import { HydratedDocument } from 'mongoose'; -import { Platform, IPlatform, IPlatformUpdateBody } from '@togethercrew.dev/db'; -import { Snowflake } from 'discord.js'; +import { type HydratedDocument } from 'mongoose'; +import { Platform, type IPlatform, type IPlatformUpdateBody } from '@togethercrew.dev/db'; +import { type Snowflake } from 'discord.js'; import { coreService } from '../../services'; import parentLogger from '../../config/logger'; @@ -12,7 +12,7 @@ const logger = parentLogger.child({ module: 'PlatformService' }); * @returns {Promise} */ async function getPlatform(filter: object): Promise | null> { - return Platform.findOne(filter); + return await Platform.findOne(filter); } /** @@ -20,7 +20,7 @@ async function getPlatform(filter: object): Promise * @param {object} filter - Filter criteria to match the desired platform entries. * @returns {Promise} - A promise that resolves to an array of matching platform entries. */ -async function getPlatforms(filter: object): Promise[]> { +async function getPlatforms(filter: object): Promise>> { return await Platform.find(filter); } @@ -30,22 +30,27 @@ async function getPlatforms(filter: object): Promise * @param {IPlatformUpdateBody} updateBody - Updated information for the platform entry. * @returns {Promise} - A promise that resolves to the updated platform entry, or null if not found. */ -async function updatePlatform(filter: object, updateBody: IPlatformUpdateBody): Promise | null> { +async function updatePlatform( + filter: object, + updateBody: IPlatformUpdateBody, +): Promise | null> { try { const platform = await Platform.findOne(filter); - if (!platform) { + if (platform === null) { return null; } + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (updateBody.metadata) { updateBody.metadata = { ...platform.metadata, - ...updateBody.metadata + ...updateBody.metadata, }; } Object.assign(platform, updateBody); - return await platform.save(); + await platform.save(); + return platform; } catch (error) { logger.error({ database: 'Core', filter, updateBody, error }, 'Failed to update platform'); return null; @@ -69,7 +74,7 @@ async function updateManyPlatforms(filter: object, updateBody: IPlatformUpdateBo } } -async function checkBotAccessToGuild(guildId: Snowflake) { +async function checkBotAccessToGuild(guildId: Snowflake): Promise { const client = await coreService.DiscordBotManager.getClient(); if (!client.guilds.cache.has(guildId)) { await updatePlatform({ 'metadata.id': guildId }, { disconnectedAt: new Date() }); diff --git a/src/database/services/rawInfo.service.ts b/src/database/services/rawInfo.service.ts index 31d42b25..4b067048 100644 --- a/src/database/services/rawInfo.service.ts +++ b/src/database/services/rawInfo.service.ts @@ -1,5 +1,6 @@ -import { Connection } from 'mongoose'; -import { IRawInfo, IRawInfoUpdateBody } from '@togethercrew.dev/db'; +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ +import { type Connection } from 'mongoose'; +import { type IRawInfo, type IRawInfoUpdateBody } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; const logger = parentLogger.child({ module: 'rawInfoService' }); @@ -14,16 +15,16 @@ async function createRawInfo(connection: Connection, rawInfo: IRawInfo): Promise return await connection.models.RawInfo.create(rawInfo); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - if (error.code == 11000) { + if (error.code === 11000) { logger.warn( { database: connection.name, channel_id: rawInfo.channelId, message_id: rawInfo.messageId }, - 'Failed to create duplicate rawInfo' + 'Failed to create duplicate rawInfo', ); return null; } logger.error( { database: connection.name, channel_id: rawInfo.channelId, message_id: rawInfo.messageId }, - 'Failed to create rawInfo' + 'Failed to create rawInfo', ); return null; } @@ -40,7 +41,7 @@ async function createRawInfos(connection: Connection, rawInfos: IRawInfo[]): Pro return await connection.models.RawInfo.insertMany(rawInfos, { ordered: false }); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - if (error.code == 11000) { + if (error.code === 11000) { logger.warn({ database: connection.name }, 'Failed to create duplicate rawInfos'); return []; } @@ -79,15 +80,16 @@ async function getRawInfos(connection: Connection, filter: object): Promise { try { const rawInfo = await connection.models.RawInfo.findOne(filter); - if (!rawInfo) { + if (rawInfo === null) { return null; } Object.assign(rawInfo, updateBody); - return await rawInfo.save(); + await rawInfo.save(); + return rawInfo; } catch (error) { logger.error({ database: connection.name, filter, updateBody, error }, 'Failed to update rawInfo'); return null; @@ -104,7 +106,7 @@ async function updateRawInfo( async function updateManyRawInfo( connection: Connection, filter: object, - updateBody: IRawInfoUpdateBody + updateBody: IRawInfoUpdateBody, ): Promise { try { const updateResult = await connection.models.RawInfo.updateMany(filter, updateBody); diff --git a/src/database/services/role.service.ts b/src/database/services/role.service.ts index 50259d62..16a7dbec 100644 --- a/src/database/services/role.service.ts +++ b/src/database/services/role.service.ts @@ -1,6 +1,7 @@ -import { Connection } from 'mongoose'; -import { IRole, IRoleMethods, IRoleUpdateBody } from '@togethercrew.dev/db'; -import { Role } from 'discord.js'; +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ +import { type Connection } from 'mongoose'; +import { type IRole, type IRoleMethods, type IRoleUpdateBody } from '@togethercrew.dev/db'; +import { type Role } from 'discord.js'; import parentLogger from '../../config/logger'; const logger = parentLogger.child({ module: 'roleService' }); @@ -15,7 +16,7 @@ async function createRole(connection: Connection, role: IRole): Promise { try { const role = await connection.models.Role.findOne(filter); - if (!role) { + if (role === null) { return null; } Object.assign(role, updateBody); - return await role.save(); + await role.save(); + return role; } catch (error) { logger.error({ database: connection.name, filter, updateBody, error }, 'Failed to update role'); return null; diff --git a/src/events/channel/channelCreate.ts b/src/events/channel/channelCreate.ts index db27d57c..dc3111fd 100644 --- a/src/events/channel/channelCreate.ts +++ b/src/events/channel/channelCreate.ts @@ -1,4 +1,4 @@ -import { Events, Channel, TextChannel, VoiceChannel, CategoryChannel } from 'discord.js'; +import { Events, type Channel, TextChannel, VoiceChannel, CategoryChannel } from 'discord.js'; import { channelService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; diff --git a/src/events/channel/channelDelete.ts b/src/events/channel/channelDelete.ts index 06896d46..5e313b81 100644 --- a/src/events/channel/channelDelete.ts +++ b/src/events/channel/channelDelete.ts @@ -1,4 +1,4 @@ -import { Events, Channel, TextChannel, VoiceChannel, CategoryChannel } from 'discord.js'; +import { Events, type Channel, TextChannel, VoiceChannel, CategoryChannel } from 'discord.js'; import { channelService, platformService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; @@ -15,15 +15,17 @@ export default { const connection = await DatabaseManager.getInstance().getTenantDb(channel.guild.id); try { const channelDoc = await channelService.getChannel(connection, { channelId: channel.id }); - await channelDoc?.softDelete(); + channelDoc?.softDelete(); const platformDoc = await platformService.getPlatform({ 'metadata.id': channel.guild.id }); const updatedSelecetdChannels = platformDoc?.metadata?.selectedChannels?.filter( // eslint-disable-next-line @typescript-eslint/no-explicit-any - (selectedChannel: any) => selectedChannel.channelId !== channel.id + (selectedChannel: any) => selectedChannel.channelId !== channel.id, + ); + await platformService.updatePlatform( + { 'metadata.id': channel.guild.id }, + { metadata: { selectedChannels: updatedSelecetdChannels } }, ); - await platformService.updatePlatform({ 'metadata.id': channel.guild.id }, { metadata: { selectedChannels: updatedSelecetdChannels } }); logger.info(logFields, 'event is done'); - } catch (err) { logger.error({ ...logFields, err }, 'Failed to soft delete the channel'); } diff --git a/src/events/channel/channelUpdate.ts b/src/events/channel/channelUpdate.ts index 3eeb302e..3c6a1183 100644 --- a/src/events/channel/channelUpdate.ts +++ b/src/events/channel/channelUpdate.ts @@ -1,4 +1,4 @@ -import { Events, Channel, TextChannel, VoiceChannel, CategoryChannel } from 'discord.js'; +import { Events, type Channel, TextChannel, VoiceChannel, CategoryChannel } from 'discord.js'; import { channelService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; @@ -20,7 +20,6 @@ export default { try { await channelService.handelChannelChanges(connection, newChannel); logger.info(logFields, 'event is done'); - } catch (err) { logger.error({ ...logFields, err }, 'Failed to handle channel changes'); } diff --git a/src/events/client/ready.ts b/src/events/client/ready.ts index 8506c04b..6e2c9fb8 100644 --- a/src/events/client/ready.ts +++ b/src/events/client/ready.ts @@ -1,4 +1,4 @@ -import { Client, Events } from 'discord.js'; +import { type Client, Events } from 'discord.js'; import { platformService } from '../../database/services'; import fetchMembers from '../../functions/fetchMembers'; import fetchChannels from '../../functions/fetchChannels'; @@ -23,10 +23,13 @@ export default { await fetchChannels(connection, platforms[i]); logger.info({ platform_id: platforms[i].metadata?.id }, 'Fetching guild members, roles, channels is done'); } catch (err) { - logger.error({ platform_id: platforms[i].metadata?.id, err }, 'Fetching guild members, roles,and channels failed'); + logger.error( + { platform_id: platforms[i].metadata?.id, err }, + 'Fetching guild members, roles,and channels failed', + ); logger.info('event is done'); } } logger.info('event is done'); }, -}; \ No newline at end of file +}; diff --git a/src/events/interaction/command.ts b/src/events/interaction/command.ts index 179d8925..c6c4a132 100644 --- a/src/events/interaction/command.ts +++ b/src/events/interaction/command.ts @@ -1,11 +1,11 @@ -import { Events, BaseInteraction } from 'discord.js'; +import { Events, type BaseInteraction } from 'discord.js'; export default { - name: Events.InteractionCreate, - once: false, - async execute(interaction: BaseInteraction) { - if (!interaction.isChatInputCommand()) return; - const command = interaction.client.commands.get(interaction.commandName); - await command.execute(interaction); - }, -}; \ No newline at end of file + name: Events.InteractionCreate, + once: false, + async execute(interaction: BaseInteraction) { + if (!interaction.isChatInputCommand()) return; + const command = interaction.client.commands.get(interaction.commandName); + await command.execute(interaction); + }, +}; diff --git a/src/events/member/guildMemberAdd.ts b/src/events/member/guildMemberAdd.ts index 0f009ae2..254245b3 100644 --- a/src/events/member/guildMemberAdd.ts +++ b/src/events/member/guildMemberAdd.ts @@ -1,4 +1,4 @@ -import { Events, GuildMember } from 'discord.js'; +import { Events, type GuildMember } from 'discord.js'; import { guildMemberService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; diff --git a/src/events/member/guildMemberRemove.ts b/src/events/member/guildMemberRemove.ts index 70360fca..87aa24d1 100644 --- a/src/events/member/guildMemberRemove.ts +++ b/src/events/member/guildMemberRemove.ts @@ -1,4 +1,4 @@ -import { Events, GuildMember } from 'discord.js'; +import { Events, type GuildMember } from 'discord.js'; import { guildMemberService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; @@ -14,7 +14,7 @@ export default { const connection = await DatabaseManager.getInstance().getTenantDb(member.guild.id); try { const guildMemberDoc = await guildMemberService.getGuildMember(connection, { discordId: member.user.id }); - await guildMemberDoc?.softDelete(); + guildMemberDoc?.softDelete(); logger.info(logFields, 'event is done'); } catch (err) { logger.error({ ...logFields, err }, 'Failed to soft delete the guild member'); diff --git a/src/events/member/guildMemberUpdate.ts b/src/events/member/guildMemberUpdate.ts index 3eef775d..2142ca55 100644 --- a/src/events/member/guildMemberUpdate.ts +++ b/src/events/member/guildMemberUpdate.ts @@ -1,4 +1,4 @@ -import { Events, GuildMember } from 'discord.js'; +import { Events, type GuildMember } from 'discord.js'; import { guildMemberService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; diff --git a/src/events/role/roleCreate.ts b/src/events/role/roleCreate.ts index 6e9f74a8..e1f08a8f 100644 --- a/src/events/role/roleCreate.ts +++ b/src/events/role/roleCreate.ts @@ -1,4 +1,4 @@ -import { Events, Role } from 'discord.js'; +import { Events, type Role } from 'discord.js'; import { roleService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; diff --git a/src/events/role/roleDelete.ts b/src/events/role/roleDelete.ts index 61678ff4..81b768a9 100644 --- a/src/events/role/roleDelete.ts +++ b/src/events/role/roleDelete.ts @@ -1,4 +1,4 @@ -import { Events, Role } from 'discord.js'; +import { Events, type Role } from 'discord.js'; import { roleService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; @@ -14,7 +14,7 @@ export default { const connection = await DatabaseManager.getInstance().getTenantDb(role.guild.id); try { const roleDoc = await roleService.getRole(connection, { roleId: role.id }); - await roleDoc?.softDelete(); + roleDoc?.softDelete(); logger.info(logFields, 'event is done'); } catch (err) { logger.error({ ...logFields, err }, 'Failed to soft delete the role'); diff --git a/src/events/role/roleUpdate.ts b/src/events/role/roleUpdate.ts index 0d26f1fd..43f51ded 100644 --- a/src/events/role/roleUpdate.ts +++ b/src/events/role/roleUpdate.ts @@ -1,4 +1,4 @@ -import { Events, Role } from 'discord.js'; +import { Events, type Role } from 'discord.js'; import { roleService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; diff --git a/src/events/user/userUpdate.ts b/src/events/user/userUpdate.ts index 1dc6724b..4f83572e 100644 --- a/src/events/user/userUpdate.ts +++ b/src/events/user/userUpdate.ts @@ -1,4 +1,4 @@ -import { Events, User } from 'discord.js'; +import { Events, type User } from 'discord.js'; import { guildMemberService, platformService } from '../../database/services'; import { DatabaseManager } from '@togethercrew.dev/db'; import parentLogger from '../../config/logger'; @@ -20,7 +20,7 @@ export default { { username: newUser.username, globalName: newUser.globalName, - } + }, ); logger.info(logFields, 'event is done'); } diff --git a/src/functions/cronJon.ts b/src/functions/cronJon.ts index 54a914af..b3acc57a 100644 --- a/src/functions/cronJon.ts +++ b/src/functions/cronJon.ts @@ -1,4 +1,4 @@ -import { Types } from 'mongoose'; +import { type Types } from 'mongoose'; import { platformService } from '../database/services'; import { ChoreographyDict, MBConnection, Status } from '@togethercrew.dev/tc-messagebroker'; import guildExtraction from './guildExtraction'; @@ -7,7 +7,7 @@ import { DatabaseManager } from '@togethercrew.dev/db'; const logger = parentLogger.child({ event: 'CronJob' }); -async function createAndStartCronJobSaga(platformId: Types.ObjectId) { +async function createAndStartCronJobSaga(platformId: Types.ObjectId): Promise { try { const saga = await MBConnection.models.Saga.create({ status: Status.NOT_STARTED, @@ -23,7 +23,7 @@ async function createAndStartCronJobSaga(platformId: Types.ObjectId) { /** * Runs the extraction process for multiple guilds. */ -export default async function cronJob() { +export default async function cronJob(): Promise { logger.info('event is running'); const platforms = await platformService.getPlatforms({ disconnectedAt: null }); for (let i = 0; i < platforms.length; i++) { @@ -36,7 +36,6 @@ export default async function cronJob() { } catch (err) { logger.error({ platform_Id: platforms[i].metadata?.id, err }, 'CronJob Failed for platform'); } - } logger.info('event is done'); } diff --git a/src/functions/fetchChannels.ts b/src/functions/fetchChannels.ts index 5d31bc7d..74fc0105 100644 --- a/src/functions/fetchChannels.ts +++ b/src/functions/fetchChannels.ts @@ -1,6 +1,6 @@ -import { TextChannel, VoiceChannel, CategoryChannel } from 'discord.js'; -import { Connection, HydratedDocument } from 'mongoose'; -import { IPlatform, IChannel } from '@togethercrew.dev/db'; +import { type TextChannel, type VoiceChannel, type CategoryChannel } from 'discord.js'; +import { type Connection, type HydratedDocument } from 'mongoose'; +import { type IPlatform, type IChannel } from '@togethercrew.dev/db'; import { channelService, platformService } from '../database/services'; import { coreService } from '../services'; import parentLogger from '../config/logger'; @@ -15,7 +15,7 @@ const logger = parentLogger.child({ module: 'FetchChannels' }); */ function pushChannelsToArray( arr: IChannel[], - channelArray: Array + channelArray: Array, ): IChannel[] { for (const channel of channelArray) { arr.push(channelService.getNeededDateFromChannel(channel)); @@ -28,11 +28,12 @@ function pushChannelsToArray( * @param {Connection} connection - Mongoose connection object for the database. * @param {Snowflake} guildId - The identifier of the guild to extract text and voice channels from. */ +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async function fetchGuildChannels(connection: Connection, platform: HydratedDocument) { try { const client = await coreService.DiscordBotManager.getClient(); const hasBotAccessToGuild = await platformService.checkBotAccessToGuild(platform.metadata?.id); - logger.info({ hasBotAccessToGuild, guildId: platform.metadata?.id, type: 'channel' }) + logger.info({ hasBotAccessToGuild, guildId: platform.metadata?.id, type: 'channel' }); if (!hasBotAccessToGuild) { return; @@ -40,10 +41,10 @@ export default async function fetchGuildChannels(connection: Connection, platfor const guild = await client.guilds.fetch(platform.metadata?.id); const channelsToStore: IChannel[] = []; const textAndVoiceChannels = [...guild.channels.cache.values()].filter( - channel => channel.type === 0 || channel.type === 2 || channel.type === 4 + (channel) => channel.type === 0 || channel.type === 2 || channel.type === 4, ) as Array; pushChannelsToArray(channelsToStore, textAndVoiceChannels); - logger.info({ channels: channelsToStore }) + logger.info({ channels: channelsToStore }); await channelService.createChannels(connection, channelsToStore); // assuming a 'channelService' } catch (error) { logger.error({ guild_id: platform.metadata?.id, error }, 'Failed to fetch channels'); diff --git a/src/functions/fetchMembers.ts b/src/functions/fetchMembers.ts index f76fc91e..1a7750ff 100644 --- a/src/functions/fetchMembers.ts +++ b/src/functions/fetchMembers.ts @@ -1,7 +1,6 @@ -import { GuildMember } from 'discord.js'; -import { Connection, HydratedDocument } from 'mongoose'; -import { IPlatform } from '@togethercrew.dev/db'; -import { IGuildMember, } from '@togethercrew.dev/db'; +import { type GuildMember } from 'discord.js'; +import { type Connection, type HydratedDocument } from 'mongoose'; +import { type IPlatform, type IGuildMember } from '@togethercrew.dev/db'; import { guildMemberService, platformService } from '../database/services'; import { coreService } from '../services'; @@ -26,12 +25,13 @@ function pushMembersToArray(arr: IGuildMember[], guildMembersArray: GuildMember[ * @param {Connection} connection - Mongoose connection object for the database. * @param {Snowflake} guildId - The identifier of the guild to extract information from. */ +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async function fetchGuildMembers(connection: Connection, platform: HydratedDocument) { try { const client = await coreService.DiscordBotManager.getClient(); const hasBotAccessToGuild = await platformService.checkBotAccessToGuild(platform.metadata?.id); - logger.info({ hasBotAccessToGuild, guildId: platform.metadata?.id, type: 'guild member' }) + logger.info({ hasBotAccessToGuild, guildId: platform.metadata?.id, type: 'guild member' }); if (!hasBotAccessToGuild) { return; diff --git a/src/functions/fetchMessages.ts b/src/functions/fetchMessages.ts index 89185e59..0e62481e 100644 --- a/src/functions/fetchMessages.ts +++ b/src/functions/fetchMessages.ts @@ -1,7 +1,18 @@ -import { Message, TextChannel, Collection, User, Role, ThreadChannel, Snowflake } from 'discord.js'; -import { IRawInfo } from '@togethercrew.dev/db'; +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +/* eslint-disable no-unneeded-ternary */ +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ +import { + type Message, + TextChannel, + type Collection, + type User, + type Role, + ThreadChannel, + type Snowflake, +} from 'discord.js'; +import { type IRawInfo } from '@togethercrew.dev/db'; import { rawInfoService } from '../database/services'; -import { Connection } from 'mongoose'; +import { type Connection } from 'mongoose'; import parentLogger from '../config/logger'; const logger = parentLogger.child({ module: 'FetchMessages' }); @@ -32,7 +43,8 @@ async function getReactions(message: Message): Promise { for (const reaction of reactionsArray) { const emoji = reaction.emoji; const users: Collection = await reaction.users.fetch(); - let usersString = users.map(user => `${user.id}`).join(','); + let usersString = users.map((user) => `${user.id}`).join(','); + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions usersString += `,${emoji.name}`; reactionsArr.push(usersString); } @@ -100,7 +112,7 @@ async function pushMessagesToArray( connection: Connection, arr: IRawInfo[], messagesArray: Message[], - threadInfo?: threadInfo + threadInfo?: threadInfo, ): Promise { const allowedTypes: number[] = [0, 18, 19]; for (const message of messagesArray) { @@ -109,7 +121,7 @@ async function pushMessagesToArray( } if (allowedTypes.includes(message.type)) { - await arr.push(await getNeedDataFromMessage(message, threadInfo)); + arr.push(await getNeedDataFromMessage(message, threadInfo)); } } return arr; @@ -129,12 +141,12 @@ async function fetchMessages( channel: TextChannel | ThreadChannel, rawInfo: IRawInfo | undefined = undefined, period: Date, - fetchDirection: 'before' | 'after' = 'before' + fetchDirection: 'before' | 'after' = 'before', ) { try { logger.info( { guild_id: connection.name, channel_id: channel.id, fetchDirection }, - 'Fetching channel messages is running' + 'Fetching channel messages is running', ); const messagesToStore: IRawInfo[] = []; const options: FetchOptions = { limit: 100 }; @@ -148,26 +160,26 @@ async function fetchMessages( if (!boundaryMessage || (period && boundaryMessage.createdAt < period)) { if (period) { - fetchedMessages = fetchedMessages.filter(msg => msg.createdAt > 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, - }) + 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, - }) + threadId: channel.id, + threadName: channel.name, + channelId: channel.parent?.id, + channelName: channel.parent?.name, + }) : await pushMessagesToArray(connection, messagesToStore, [...fetchedMessages.values()]); await rawInfoService.createRawInfos(connection, messagesToStore); options[fetchDirection] = boundaryMessage.id; @@ -176,12 +188,12 @@ async function fetchMessages( } catch (err) { logger.error( { guild_id: connection.name, channel_id: channel.id, fetchDirection, err }, - 'Fetching channel messages failed' + 'Fetching channel messages failed', ); } logger.info( { guild_id: connection.name, channel_id: channel.id, fetchDirection }, - 'Fetching channel messages is done' + 'Fetching channel messages is done', ); } diff --git a/src/functions/fetchRoles.ts b/src/functions/fetchRoles.ts index 32a475dc..63137671 100644 --- a/src/functions/fetchRoles.ts +++ b/src/functions/fetchRoles.ts @@ -1,11 +1,10 @@ -import { Role } from 'discord.js'; -import { Connection, HydratedDocument } from 'mongoose'; -import { IPlatform, IRole } from '@togethercrew.dev/db'; +import { type Role } from 'discord.js'; +import { type Connection, type HydratedDocument } from 'mongoose'; +import { type IPlatform, type IRole } from '@togethercrew.dev/db'; import { roleService, platformService } from '../database/services'; import parentLogger from '../config/logger'; import { coreService } from '../services'; - const logger = parentLogger.child({ module: 'FetchRoles' }); /** @@ -26,11 +25,12 @@ function pushRolesToArray(arr: IRole[], roleArray: Role[]): IRole[] { * @param {Connection} connection - Mongoose connection object for the database. * @param {Snowflake} guildId - The identifier of the guild to extract roles from. */ +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async function fetchGuildRoles(connection: Connection, platform: HydratedDocument) { try { const client = await coreService.DiscordBotManager.getClient(); const hasBotAccessToGuild = await platformService.checkBotAccessToGuild(platform.metadata?.id); - logger.info({ hasBotAccessToGuild, guildId: platform.metadata?.id, type: 'role' }) + logger.info({ hasBotAccessToGuild, guildId: platform.metadata?.id, type: 'role' }); if (!hasBotAccessToGuild) { return; diff --git a/src/functions/guildExtraction.ts b/src/functions/guildExtraction.ts index 507fc7c8..7775ebc5 100644 --- a/src/functions/guildExtraction.ts +++ b/src/functions/guildExtraction.ts @@ -1,6 +1,5 @@ - -import { Connection, HydratedDocument } from 'mongoose'; -import { IPlatform } from '@togethercrew.dev/db'; +import { type Connection, type HydratedDocument } from 'mongoose'; +import { type IPlatform } from '@togethercrew.dev/db'; import { platformService } from '../database/services'; import handleFetchChannelMessages from './fetchMessages'; import parentLogger from '../config/logger'; @@ -12,6 +11,7 @@ const logger = parentLogger.child({ module: 'GuildExtraction' }); * @param {Connection} connection - Mongoose connection object for the database. * @param {Snowflake} guildId - The identifier of the guild to extract information from. */ +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async function guildExtraction(connection: Connection, platform: HydratedDocument) { const client = await coreService.DiscordBotManager.getClient(); @@ -22,11 +22,13 @@ export default async function guildExtraction(connection: Connection, platform: return; } const guild = await client.guilds.fetch(platform.metadata?.id); + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (platform.metadata?.selectedChannels && platform.metadata?.period) { await platformService.updatePlatform({ _id: platform.id }, { metadata: { isInProgress: true } }); for (let i = 0; i < platform.metadata?.selectedChannels.length; i++) { const channel = await guild.channels.fetch(platform.metadata?.selectedChannels[i]); + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (channel) { if (channel.type !== 0) continue; await handleFetchChannelMessages(connection, channel, platform.metadata?.period); diff --git a/src/functions/sendDirectMessage.ts b/src/functions/sendDirectMessage.ts index b25e958f..7b08090b 100644 --- a/src/functions/sendDirectMessage.ts +++ b/src/functions/sendDirectMessage.ts @@ -1,4 +1,4 @@ -import { Snowflake } from 'discord.js'; +import { type Snowflake } from 'discord.js'; import { coreService } from '../services'; /** @@ -6,6 +6,7 @@ import { coreService } from '../services'; * @param info * @returns throw error if User has DMs closed or has no mutual servers with the bot */ +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async function sendDirectMessage(info: { discordId: Snowflake; message: string }) { const client = await coreService.DiscordBotManager.getClient(); diff --git a/src/functions/thread.ts b/src/functions/thread.ts index 71dcc76d..9975ba77 100644 --- a/src/functions/thread.ts +++ b/src/functions/thread.ts @@ -1,4 +1,4 @@ -import { ChannelType, TextChannel } from 'discord.js'; +import { ChannelType, type TextChannel } from 'discord.js'; /** * create a private thread on specific channel and send a message to it @@ -6,9 +6,10 @@ import { ChannelType, TextChannel } from 'discord.js'; * @param info * @returns thread object */ +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export async function createPrivateThreadAndSendMessage( channel: TextChannel, - info: { threadName: string; message: string; threadReason?: string } + info: { threadName: string; message: string; threadReason?: string }, ) { const { threadName, message, threadReason } = info; diff --git a/src/index.ts b/src/index.ts index 9f032ee5..1084f4b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,16 @@ import { connectToMongoDB, connectToMB } from './database/connection'; -import { coreService } from './services'; import { addCronJob } from './queue/queues/cronJob'; import { connectToRabbitMQ } from './rabbitmq/RabbitMQConnection'; import { setupRabbitMQHandlers } from './rabbitmq/RabbitMQHandler'; -import { commandService, eventService } from './services'; +import { commandService, eventService, coreService } from './services'; +import parentLogger from './config/logger'; import './queue/workers/cronWorker'; import './queue/workers/channelMessageWorker'; import './queue/workers/directMessageWorker'; -async function app() { +const logger = parentLogger.child({ module: `app` }); + +async function app(): Promise { await connectToMongoDB(); await connectToMB(); await connectToRabbitMQ(); @@ -21,4 +23,6 @@ async function app() { addCronJob(); } -app(); +app().catch((error) => { + logger.fatal({ error }, 'Failed to start the application'); +}); diff --git a/src/interfaces/Hivemind.interface.ts b/src/interfaces/Hivemind.interface.ts index 880cd97a..4c76a5f1 100644 --- a/src/interfaces/Hivemind.interface.ts +++ b/src/interfaces/Hivemind.interface.ts @@ -2,223 +2,221 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ // InteractionResponseType export enum InteractionResponseType { - PONG = 1, - CHANNEL_MESSAGE_WITH_SOURCE = 4, - DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE = 5, - DEFERRED_UPDATE_MESSAGE = 6, - UPDATE_MESSAGE = 7, - APPLICATION_COMMAND_AUTOCOMPLETE_RESULT = 8, - MODAL = 9, - PREMIUM_REQUIRED = 10 + PONG = 1, + CHANNEL_MESSAGE_WITH_SOURCE = 4, + DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE = 5, + DEFERRED_UPDATE_MESSAGE = 6, + UPDATE_MESSAGE = 7, + APPLICATION_COMMAND_AUTOCOMPLETE_RESULT = 8, + MODAL = 9, + PREMIUM_REQUIRED = 10, } // InteractionType export enum InteractionType { - PING = 1, - APPLICATION_COMMAND = 2, - MESSAGE_COMPONENT = 3, - APPLICATION_COMMAND_AUTOCOMPLETE = 4, - MODAL_SUBMIT = 5 + PING = 1, + APPLICATION_COMMAND = 2, + MESSAGE_COMPONENT = 3, + APPLICATION_COMMAND_AUTOCOMPLETE = 4, + MODAL_SUBMIT = 5, } // BitFields interface BitFields { - bitfield?: number; - flags?: string; + bitfield?: number; + flags?: string; } // PermissionsBitField interface PermissionsBitField { - bitfield?: number; - allPermissions?: number; - defaultPermissions?: number; - permissionFlags?: { [key: string]: boolean }; - stageModerator?: number; + bitfield?: number; + allPermissions?: number; + defaultPermissions?: number; + permissionFlags?: Record; + stageModerator?: number; } // User interface User { - id: string; - createdAt?: string; - client?: Record; - defaultAvatarUrl?: string; - partial?: boolean; - createdTimestamp?: number; - discriminator?: string; - bot?: boolean; - avatar?: string; - avatarDecoration?: string; - banner?: string; - accentColor?: number; - displayName?: string; - dmChannel?: Record; - flags?: number; - globalName?: string; - hexAccentColor?: string; - system?: boolean; - tag?: string; - username?: string; + id: string; + createdAt?: string; + client?: Record; + defaultAvatarUrl?: string; + partial?: boolean; + createdTimestamp?: number; + discriminator?: string; + bot?: boolean; + avatar?: string; + avatarDecoration?: string; + banner?: string; + accentColor?: number; + displayName?: string; + dmChannel?: Record; + flags?: number; + globalName?: string; + hexAccentColor?: string; + system?: boolean; + tag?: string; + username?: string; } // Guild interface Guild { - id?: string; - name?: string; - icon?: string; - features?: string[]; - available?: boolean; - shardId?: number; - splash?: string; - banner?: string; - description?: string; - verificationLevel?: number; - vanityURLCode?: string; - nsfwLevel?: number; - premiumSubscriptionCount?: number; - discoverySplash?: string; - memberCount?: number; - large?: boolean; - premiumProgressBarEnabled?: boolean; - applicationId?: string; - afkTimeout?: number; - afkChannelId?: string; - systemChannelId?: string; - premiumTier?: number; - widgetEnabled?: boolean; - widgetChannelId?: string; - explicitContentFilter?: number; - mfaLevel?: number; - joinedTimestamp?: number; - defaultMessageNotifications?: number; - maximumMembers?: number; - maxVideoChannelUsers?: number; - maxStageVideoChannelUsers?: number; - approximateMemberCount?: number; - approximatePresenceCount?: number; - vanityURLUses?: number; - rulesChannelId?: string; - publicUpdatesChannelId?: string; - preferredLocale?: string; - safetyAlertsChannelId?: string; - ownerId?: string; + id?: string; + name?: string; + icon?: string; + features?: string[]; + available?: boolean; + shardId?: number; + splash?: string; + banner?: string; + description?: string; + verificationLevel?: number; + vanityURLCode?: string; + nsfwLevel?: number; + premiumSubscriptionCount?: number; + discoverySplash?: string; + memberCount?: number; + large?: boolean; + premiumProgressBarEnabled?: boolean; + applicationId?: string; + afkTimeout?: number; + afkChannelId?: string; + systemChannelId?: string; + premiumTier?: number; + widgetEnabled?: boolean; + widgetChannelId?: string; + explicitContentFilter?: number; + mfaLevel?: number; + joinedTimestamp?: number; + defaultMessageNotifications?: number; + maximumMembers?: number; + maxVideoChannelUsers?: number; + maxStageVideoChannelUsers?: number; + approximateMemberCount?: number; + approximatePresenceCount?: number; + vanityURLUses?: number; + rulesChannelId?: string; + publicUpdatesChannelId?: string; + preferredLocale?: string; + safetyAlertsChannelId?: string; + ownerId?: string; } // AllowedMentions interface AllowedMentions { - parse?: string[]; - roles?: number[]; - users?: string[]; - replied_user?: boolean; + parse?: string[]; + roles?: number[]; + users?: string[]; + replied_user?: boolean; } // Embed interface Attachment { - id: string; - filename?: string; - description?: string; - content_type?: string; - size?: number; - url?: string; - proxy_url?: string; - height?: number; - width?: number; - ephemeral?: boolean; - duration_secs?: number; - waveform?: string; - flags?: number; + id: string; + filename?: string; + description?: string; + content_type?: string; + size?: number; + url?: string; + proxy_url?: string; + height?: number; + width?: number; + ephemeral?: boolean; + duration_secs?: number; + waveform?: string; + flags?: number; } // Embed interface Embed { - title?: string; - type?: string; - description?: string; - url?: string; - timestamp?: string; - color?: number; - footer?: Record; - image?: Record; - thumbnail?: Record; - video?: Record; - provider?: Record; - author?: Record; - fields?: Record[]; + title?: string; + type?: string; + description?: string; + url?: string; + timestamp?: string; + color?: number; + footer?: Record; + image?: Record; + thumbnail?: Record; + video?: Record; + provider?: Record; + author?: Record; + fields?: Array>; } // InteractionCallbackData interface InteractionCallbackData { - tts?: boolean | null; - content?: string | null; - embeds?: Embed[] | null; - allowed_mentions?: string | null; - flags?: number | null; - components?: { [key: string]: any }[] | null; - attachments?: Attachment[] | null; + tts?: boolean | null; + content?: string | null; + embeds?: Embed[] | null; + allowed_mentions?: string | null; + flags?: number | null; + components?: Array> | null; + attachments?: Attachment[] | null; } // ChatInputCommandInteraction export interface ChatInputCommandInteraction_broker { - id: string; - applicationId?: string | null; - type?: number | null; - channel?: Record | null; // ? - channelId?: string | null; - token?: string | null; - guildId?: string | null; - user?: Record | null; - createdAt?: Date | null; - deferred?: boolean | null; - replied?: boolean | null; - webhook?: Record | null; - member?: Record | null; - ephemeral?: boolean | null; - guild?: Record | null; // ? - createdTimestamp?: number | null; - appPermissions?: PermissionsBitField | null; - locale?: string | null; - guildLocale?: string | null; - client?: Record | null; - command?: Record | null; - commandId?: string | null; - commandName?: string | null; - commandType?: any | null; - commandGuildId?: string | null; - memberPermissions?: PermissionsBitField | null; - options?: Record | null; - version?: number | null; + id: string; + applicationId?: string | null; + type?: number | null; + channel?: Record | null; // ? + channelId?: string | null; + token?: string | null; + guildId?: string | null; + user?: Record | null; + createdAt?: Date | null; + deferred?: boolean | null; + replied?: boolean | null; + webhook?: Record | null; + member?: Record | null; + ephemeral?: boolean | null; + guild?: Record | null; // ? + createdTimestamp?: number | null; + appPermissions?: PermissionsBitField | null; + locale?: string | null; + guildLocale?: string | null; + client?: Record | null; + command?: Record | null; + commandId?: string | null; + commandName?: string | null; + commandType?: any | null; + commandGuildId?: string | null; + memberPermissions?: PermissionsBitField | null; + options?: Record | null; + version?: number | null; } // FollowUpMessageData export interface FollowUpMessageData { - content?: string; - username?: string; - avatar_url?: string; - tts?: boolean; - embeds?: Embed[]; - allowed_mentions?: AllowedMentions; - components?: any[]; - files?: any[]; // - payload_json?: string; - attachments?: Attachment[]; - flags?: number; - thread_name?: string; + content?: string; + username?: string; + avatar_url?: string; + tts?: boolean; + embeds?: Embed[]; + allowed_mentions?: AllowedMentions; + components?: any[]; + files?: any[]; // + payload_json?: string; + attachments?: Attachment[]; + flags?: number; + thread_name?: string; } - // InteractionResponseEditData export interface InteractionResponseEditData { - thread_id?: number | null; - content?: string | null; - embeds?: Embed[] | null; - allowed_mentions?: AllowedMentions | null; - components?: { [key: string]: any }[][] | null; - files?: { [key: string]: any }[] | null; - payload_json?: string | null; - attachments?: Attachment[] | null; + thread_id?: number | null; + content?: string | null; + embeds?: Embed[] | null; + allowed_mentions?: AllowedMentions | null; + components?: Array>> | null; + files?: Array> | null; + payload_json?: string | null; + attachments?: Attachment[] | null; } - // InteractionResponse export interface InteractionResponse { - type: number; - data?: InteractionCallbackData | null; -} \ No newline at end of file + type: number; + data?: InteractionCallbackData | null; +} diff --git a/src/migrations/db/1695210587863-add-isgeneratedbyweebhook-to-rawinfo-schema.ts b/src/migrations/db/1695210587863-add-isgeneratedbyweebhook-to-rawinfo-schema.ts index 022b573b..94a93659 100644 --- a/src/migrations/db/1695210587863-add-isgeneratedbyweebhook-to-rawinfo-schema.ts +++ b/src/migrations/db/1695210587863-add-isgeneratedbyweebhook-to-rawinfo-schema.ts @@ -4,8 +4,7 @@ import { connectToMongoDB } from '../../database/connection'; import webhookLogic from '../utils/webhookLogic'; import { DatabaseManager } from '@togethercrew.dev/db'; - -export const up = async () => { +export const up = async (): Promise => { await connectToMongoDB(); const platforms = await platformService.getPlatforms({}); for (let i = 0; i < platforms.length; i++) { @@ -14,6 +13,6 @@ export const up = async () => { } }; -export const down = async () => { +export const down = async (): Promise => { // TODO: Implement rollback logic if needed }; diff --git a/src/migrations/utils/template.ts b/src/migrations/utils/template.ts index 61c4ec83..2d0f998a 100644 --- a/src/migrations/utils/template.ts +++ b/src/migrations/utils/template.ts @@ -2,13 +2,13 @@ import { connectToMongoDB } from '../../database/connection'; import 'dotenv/config'; import { DatabaseManager } from '@togethercrew.dev/db'; -export const up = async () => { +export const up = async (): Promise => { await connectToMongoDB(); - const connection = await DatabaseManager.getInstance().getTenantDb("681946187490000803"); + const connection = await DatabaseManager.getInstance().getTenantDb('681946187490000803'); await connection.createCollection('my_collection'); }; -export const down = async () => { +export const down = async (): Promise => { await connectToMongoDB(); }; diff --git a/src/migrations/utils/ts-compiler.js b/src/migrations/utils/ts-compiler.js index 07ca65c5..1424f7e7 100644 --- a/src/migrations/utils/ts-compiler.js +++ b/src/migrations/utils/ts-compiler.js @@ -1,3 +1,3 @@ // eslint-disable-next-line @typescript-eslint/no-var-requires const tsNode = require('ts-node'); -module.exports = tsNode.register; \ No newline at end of file +module.exports = tsNode.register; diff --git a/src/migrations/utils/webhookLogic.ts b/src/migrations/utils/webhookLogic.ts index c2138dca..628baef0 100644 --- a/src/migrations/utils/webhookLogic.ts +++ b/src/migrations/utils/webhookLogic.ts @@ -1,6 +1,8 @@ -import { TextChannel, Message, ThreadChannel, Snowflake } from 'discord.js'; -import { IRawInfo } from '@togethercrew.dev/db'; -import { Connection } from 'mongoose'; +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { type TextChannel, type Message, type ThreadChannel, type Snowflake } from 'discord.js'; +import { type IRawInfo } from '@togethercrew.dev/db'; +import { type Connection } from 'mongoose'; import parentLogger from '../../config/logger'; import { rawInfoService, channelService } from '../../database/services'; import { coreService } from '../../services'; @@ -17,7 +19,7 @@ async function fetchMessagesBetweenOldestAndNewest( connection: Connection, channel: TextChannel | ThreadChannel, oldestRawInfo: IRawInfo, - newestRawInfo: IRawInfo + newestRawInfo: IRawInfo, ) { try { let allMessages: Message[] = []; @@ -65,7 +67,7 @@ async function migrateIsGeneratedByWebhook(connection: Connection, channel: Text connection, channel, oldestChannelRawInfo, - newestChannelRawInfo + newestChannelRawInfo, ); const messagesToUpdateTrue = []; const messagesToUpdateFalse = []; @@ -93,7 +95,7 @@ async function migrateIsGeneratedByWebhook(connection: Connection, channel: Text await rawInfoService.updateManyRawInfo( connection, { messageId: { $in: messagesToUpdateTrue } }, - { isGeneratedByWebhook: true } + { isGeneratedByWebhook: true }, ); } @@ -101,7 +103,7 @@ async function migrateIsGeneratedByWebhook(connection: Connection, channel: Text await rawInfoService.updateManyRawInfo( connection, { messageId: { $in: messagesToUpdateFalse } }, - { isGeneratedByWebhook: false } + { isGeneratedByWebhook: false }, ); } @@ -127,7 +129,7 @@ async function migrateIsGeneratedByWebhook(connection: Connection, channel: Text connection, thread, oldestThreadRawInfo, - newestThreadRawInfo + newestThreadRawInfo, ); const threadMessagesToUpdateTrue = []; @@ -156,7 +158,7 @@ async function migrateIsGeneratedByWebhook(connection: Connection, channel: Text await rawInfoService.updateManyRawInfo( connection, { messageId: { $in: threadMessagesToUpdateTrue } }, - { isGeneratedByWebhook: true } + { isGeneratedByWebhook: true }, ); } @@ -164,7 +166,7 @@ async function migrateIsGeneratedByWebhook(connection: Connection, channel: Text await rawInfoService.updateManyRawInfo( connection, { messageId: { $in: threadMessagesToUpdateFalse } }, - { isGeneratedByWebhook: false } + { isGeneratedByWebhook: false }, ); } } @@ -173,7 +175,7 @@ async function migrateIsGeneratedByWebhook(connection: Connection, channel: Text } catch (err) { logger.error( { guild_id: connection.name, channel_id: channel.id, err }, - 'Migration for isGeneratedByWebhook failed' + 'Migration for isGeneratedByWebhook failed', ); } } diff --git a/src/queue/queues/channelMessage.ts b/src/queue/queues/channelMessage.ts index 911b2b0e..683e047e 100644 --- a/src/queue/queues/channelMessage.ts +++ b/src/queue/queues/channelMessage.ts @@ -1,8 +1,8 @@ -import { Snowflake } from 'discord.js'; +import { type Snowflake } from 'discord.js'; import { QueueFactory } from './index'; export const channelMessageQueue = QueueFactory.createQueue('channelMessageQueue'); -export const addChannelMessage = (discordId: Snowflake, message: string) => { - channelMessageQueue.add('channelMessageQueue', { discordId, message }); -}; \ No newline at end of file +export const addChannelMessage = (discordId: Snowflake, message: string): void => { + void channelMessageQueue.add('channelMessageQueue', { discordId, message }); +}; diff --git a/src/queue/queues/cronJob.ts b/src/queue/queues/cronJob.ts index 54e63e52..d16e6e1f 100644 --- a/src/queue/queues/cronJob.ts +++ b/src/queue/queues/cronJob.ts @@ -3,6 +3,7 @@ import { cronJobConfig } from '../../config/queue'; export const cronJobQueue = QueueFactory.createQueue('cronJobQueue'); -export const addCronJob = () => { - cronJobQueue.add('cronJob', {}, { repeat: cronJobConfig } as never); -}; \ No newline at end of file +export const addCronJob = (): void => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + void cronJobQueue.add('cronJob', {}, { repeat: cronJobConfig } as never); +}; diff --git a/src/queue/queues/directMessage.ts b/src/queue/queues/directMessage.ts index d48db693..f2b8883a 100644 --- a/src/queue/queues/directMessage.ts +++ b/src/queue/queues/directMessage.ts @@ -1,8 +1,8 @@ -import { Snowflake } from 'discord.js'; +import { type Snowflake } from 'discord.js'; import { QueueFactory } from './index'; export const directMessageQueue = QueueFactory.createQueue('directMessageQueue'); -export const addDirectMessage = (discordId: Snowflake, message: string) => { - directMessageQueue.add('directMessageQueue', { discordId, message }); -}; \ No newline at end of file +export const addDirectMessage = (discordId: Snowflake, message: string): void => { + void directMessageQueue.add('directMessageQueue', { discordId, message }); +}; diff --git a/src/queue/queues/index.ts b/src/queue/queues/index.ts index 610d715d..c85e227f 100644 --- a/src/queue/queues/index.ts +++ b/src/queue/queues/index.ts @@ -1,8 +1,9 @@ import { Queue } from 'bullmq'; import { redisConfig } from '../../config/queue'; +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class QueueFactory { - static createQueue(name: string): Queue { - return new Queue(name, { connection: redisConfig }); - } -} \ No newline at end of file + static createQueue(name: string): Queue { + return new Queue(name, { connection: redisConfig }); + } +} diff --git a/src/queue/workers/channelMessageWorker.ts b/src/queue/workers/channelMessageWorker.ts index 227e6e43..3f766ec5 100644 --- a/src/queue/workers/channelMessageWorker.ts +++ b/src/queue/workers/channelMessageWorker.ts @@ -4,16 +4,16 @@ import { redisConfig, rateLimitConfig } from '../../config/queue'; import { channelService } from '../../services'; export const channelMessageWorker = new Worker( - 'channelMessageQueue', - async (job: Job | undefined) => { - if (job) { - await channelService.sendChannelMessage(job.data.discordId, job.data.message); - } - }, - { - connection: redisConfig, - limiter: rateLimitConfig + 'channelMessageQueue', + async (job: Job | undefined) => { + if (job) { + await channelService.sendChannelMessage(job.data.discordId, job.data.message); } + }, + { + connection: redisConfig, + limiter: rateLimitConfig, + }, ); -WorkerFactory.attachEventListeners(channelMessageWorker) +WorkerFactory.attachEventListeners(channelMessageWorker); diff --git a/src/queue/workers/cronWorker.ts b/src/queue/workers/cronWorker.ts index 30fa659d..16990b72 100644 --- a/src/queue/workers/cronWorker.ts +++ b/src/queue/workers/cronWorker.ts @@ -4,16 +4,16 @@ import { redisConfig } from '../../config/queue'; import cronJob from '../../functions/cronJon'; export const cronWorker = new Worker( - 'cronJobQueue', - async (job: Job | undefined) => { - if (job) { - await cronJob(); - } - }, - { - connection: redisConfig, - lockDuration: 79200000, // 22 hours + 'cronJobQueue', + async (job: Job | undefined) => { + if (job) { + await cronJob(); } + }, + { + connection: redisConfig, + lockDuration: 79200000, // 22 hours + }, ); -WorkerFactory.attachEventListeners(cronWorker) +WorkerFactory.attachEventListeners(cronWorker); diff --git a/src/queue/workers/directMessageWorker.ts b/src/queue/workers/directMessageWorker.ts index 7184777a..685c2b1c 100644 --- a/src/queue/workers/directMessageWorker.ts +++ b/src/queue/workers/directMessageWorker.ts @@ -4,16 +4,16 @@ import { redisConfig, rateLimitConfig } from '../../config/queue'; import { userService } from '../../services'; export const directMessageWorker = new Worker( - 'directMessageQueue', - async (job: Job | undefined) => { - if (job) { - await userService.sendDirectMessage(job.data.discordId, job.data.message); - } - }, - { - connection: redisConfig, - limiter: rateLimitConfig + 'directMessageQueue', + async (job: Job | undefined) => { + if (job) { + await userService.sendDirectMessage(job.data.discordId, job.data.message); } + }, + { + connection: redisConfig, + limiter: rateLimitConfig, + }, ); -WorkerFactory.attachEventListeners(directMessageWorker) +WorkerFactory.attachEventListeners(directMessageWorker); diff --git a/src/queue/workers/index.ts b/src/queue/workers/index.ts index 2fbdaae8..3e8d2ec6 100644 --- a/src/queue/workers/index.ts +++ b/src/queue/workers/index.ts @@ -3,13 +3,13 @@ import parentLogger from '../../config/logger'; const logger = parentLogger.child({ module: 'Queue' }); export class WorkerFactory { - static attachEventListeners(worker: Worker) { - worker.on('completed', job => { - logger.info({ job }, 'Job is done'); - }); + static attachEventListeners(worker: Worker) { + worker.on('completed', (job) => { + logger.info({ job }, 'Job is done'); + }); - worker.on('failed', (job, error) => { - logger.error({ job, error }, 'Job failed'); - }); - } -} \ No newline at end of file + worker.on('failed', (job, error) => { + logger.error({ job, error }, 'Job failed'); + }); + } +} diff --git a/src/rabbitmq/RabbitMQConnection.ts b/src/rabbitmq/RabbitMQConnection.ts index b7454c4e..5463a493 100644 --- a/src/rabbitmq/RabbitMQConnection.ts +++ b/src/rabbitmq/RabbitMQConnection.ts @@ -3,11 +3,11 @@ import config from '../config'; import logger from '../config/logger'; // Connect to RabbitMQ -export const connectToRabbitMQ = async () => { - try { - await RabbitMQ.connect(config.rabbitMQ.url, Queue.DISCORD_BOT); - logger.info({ url: config.rabbitMQ.url, queue: Queue.DISCORD_BOT }, 'Connected to RabbitMQ!'); - } catch (error) { - logger.fatal({ url: config.rabbitMQ.url, queue: Queue.DISCORD_BOT, error }, 'Failed to connect to RabbitMQ!') - } -}; \ No newline at end of file +export const connectToRabbitMQ = async (): Promise => { + try { + await RabbitMQ.connect(config.rabbitMQ.url, Queue.DISCORD_BOT); + logger.info({ url: config.rabbitMQ.url, queue: Queue.DISCORD_BOT }, 'Connected to RabbitMQ!'); + } catch (error) { + logger.fatal({ url: config.rabbitMQ.url, queue: Queue.DISCORD_BOT, error }, 'Failed to connect to RabbitMQ!'); + } +}; diff --git a/src/rabbitmq/RabbitMQHandler.ts b/src/rabbitmq/RabbitMQHandler.ts index f75c76cc..fc303368 100644 --- a/src/rabbitmq/RabbitMQHandler.ts +++ b/src/rabbitmq/RabbitMQHandler.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ import RabbitMQ, { Event } from '@togethercrew.dev/tc-messagebroker'; import { handleFetchEvent } from './events/fetchEvent'; import { handleSendMessageEvent } from './events/sendMessageEvent'; @@ -8,13 +9,13 @@ import { handleInteractionResponseDelete } from './events/interactionResponseDel import { handleFollowUpMessageCreate } from './events/FollowUpMessageCreate'; import { handleSendMessageToChannel } from './events/sendMessageToChannel'; -export function setupRabbitMQHandlers() { - RabbitMQ.onEvent(Event.DISCORD_BOT.FETCH, handleFetchEvent); - RabbitMQ.onEvent(Event.DISCORD_BOT.SEND_MESSAGE, handleSendMessageEvent); - RabbitMQ.onEvent(Event.DISCORD_BOT.SEND_MESSAGE_TO_CHANNEL, handleSendMessageToChannel); - RabbitMQ.onEvent(Event.DISCORD_BOT.FETCH_MEMBERS, handleFetchMembersEvent); - RabbitMQ.onEvent(Event.DISCORD_BOT.INTERACTION_RESPONSE.CREATE, handleInteractionResponseCreate); - RabbitMQ.onEvent(Event.DISCORD_BOT.INTERACTION_RESPONSE.EDIT, handleInteractionResponseEdit); - RabbitMQ.onEvent(Event.DISCORD_BOT.INTERACTION_RESPONSE.DELETE, handleInteractionResponseDelete); - RabbitMQ.onEvent(Event.DISCORD_BOT.FOLLOWUP_MESSAGE.CREATE, handleFollowUpMessageCreate); -} \ No newline at end of file +export function setupRabbitMQHandlers(): void { + RabbitMQ.onEvent(Event.DISCORD_BOT.FETCH, handleFetchEvent); + RabbitMQ.onEvent(Event.DISCORD_BOT.SEND_MESSAGE, handleSendMessageEvent); + RabbitMQ.onEvent(Event.DISCORD_BOT.SEND_MESSAGE_TO_CHANNEL, handleSendMessageToChannel); + RabbitMQ.onEvent(Event.DISCORD_BOT.FETCH_MEMBERS, handleFetchMembersEvent); + RabbitMQ.onEvent(Event.DISCORD_BOT.INTERACTION_RESPONSE.CREATE, handleInteractionResponseCreate); + RabbitMQ.onEvent(Event.DISCORD_BOT.INTERACTION_RESPONSE.EDIT, handleInteractionResponseEdit); + RabbitMQ.onEvent(Event.DISCORD_BOT.INTERACTION_RESPONSE.DELETE, handleInteractionResponseDelete); + RabbitMQ.onEvent(Event.DISCORD_BOT.FOLLOWUP_MESSAGE.CREATE, handleFollowUpMessageCreate); +} diff --git a/src/rabbitmq/events/FollowUpMessageCreate.ts b/src/rabbitmq/events/FollowUpMessageCreate.ts index 43a87f8f..1dbdeb56 100644 --- a/src/rabbitmq/events/FollowUpMessageCreate.ts +++ b/src/rabbitmq/events/FollowUpMessageCreate.ts @@ -1,22 +1,21 @@ import { Event } from '@togethercrew.dev/tc-messagebroker'; -import { ChatInputCommandInteraction_broker, FollowUpMessageData } from '../../interfaces/Hivemind.interface'; import { interactionService } from '../../services'; import parentLogger from '../../config/logger'; const logger = parentLogger.child({ module: `${Event.DISCORD_BOT.FOLLOWUP_MESSAGE.CREATE}` }); // eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function handleFollowUpMessageCreate(msg: any) { - try { - logger.info({ msg, event: Event.DISCORD_BOT.FOLLOWUP_MESSAGE.CREATE }, 'is running'); - // const interaction: ChatInputCommandInteraction_broker = JSON.parse(msg?.content.interaction); - // const data: FollowUpMessageData = JSON.parse(msg?.content.data); +export async function handleFollowUpMessageCreate(msg: any): Promise { + try { + logger.info({ msg, event: Event.DISCORD_BOT.FOLLOWUP_MESSAGE.CREATE }, 'is running'); + // const interaction: ChatInputCommandInteraction_broker = JSON.parse(msg?.content.interaction); + // const data: FollowUpMessageData = JSON.parse(msg?.content.data); - const interaction = msg?.content.interaction; - const data = msg?.content.data; + const interaction = msg?.content.interaction; + const data = msg?.content.data; - await interactionService.createFollowUpMessage(interaction, data) - logger.info({ msg, event: Event.DISCORD_BOT.FOLLOWUP_MESSAGE.CREATE }, 'is done'); - } catch (error) { - logger.error({ msg, event: Event.DISCORD_BOT.FOLLOWUP_MESSAGE.CREATE, error }, 'is failed'); - } -} \ No newline at end of file + await interactionService.createFollowUpMessage(interaction, data); + logger.info({ msg, event: Event.DISCORD_BOT.FOLLOWUP_MESSAGE.CREATE }, 'is done'); + } catch (error) { + logger.error({ msg, event: Event.DISCORD_BOT.FOLLOWUP_MESSAGE.CREATE, error }, 'is failed'); + } +} diff --git a/src/rabbitmq/events/fetchEvent.ts b/src/rabbitmq/events/fetchEvent.ts index 2d685eaf..8c1466c0 100644 --- a/src/rabbitmq/events/fetchEvent.ts +++ b/src/rabbitmq/events/fetchEvent.ts @@ -8,42 +8,39 @@ import parentLogger from '../../config/logger'; import { platformService } from '../../database/services'; const logger = parentLogger.child({ module: `${Event.DISCORD_BOT.FETCH}` }); +const fetchMethod = async (msg: any): Promise => { + logger.info({ msg }, 'fetchMethod is running'); + if (msg === undefined || msg === null) return; + const { content } = msg; + const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); + logger.info({ saga: saga.data }, 'the saga info'); + const platformId = saga.data.platformId; + const platform = await platformService.getPlatform({ _id: platformId }); -const fetchMethod = async (msg: any) => { - - logger.info({ msg }, 'fetchMethod is running'); - if (!msg) return; - const { content } = msg; - const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); - logger.info({ saga: saga.data }, 'the saga info'); - const platformId = saga.data['platformId']; - const platform = await platformService.getPlatform({ _id: platformId }); - - if (platform) { - const isPlatformCreated = saga.data['created']; - const connection = await DatabaseManager.getInstance().getTenantDb(platform.metadata?.id); - if (isPlatformCreated) { - await fetchChannels(connection, platform); - await fetchMembers(connection, platform); - await fetchRoles(connection, platform); - } else { - await guildExtraction(connection, platform); - } + if (platform !== null) { + const isPlatformCreated = saga.data.created; + const connection = await DatabaseManager.getInstance().getTenantDb(platform.metadata?.id); + if (isPlatformCreated === true) { + await fetchChannels(connection, platform); + await fetchMembers(connection, platform); + await fetchRoles(connection, platform); + } else { + await guildExtraction(connection, platform); } - logger.info({ msg }, 'fetchMethod is done'); + } + logger.info({ msg }, 'fetchMethod is done'); }; -export async function handleFetchEvent(msg: any) { - try { - logger.info({ msg, event: Event.DISCORD_BOT.FETCH, sagaId: msg.content.uuid }, 'is running'); - if (!msg) return; - - const { content } = msg; - const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); - await saga.next(() => fetchMethod(msg)); - logger.info({ msg, event: Event.DISCORD_BOT.FETCH, sagaId: msg.content.uuid }, 'is done'); - } catch (error) { - logger.error({ msg, event: Event.DISCORD_BOT.FETCH, sagaId: msg.content.uuid, error }, 'is failed'); - - } -} \ No newline at end of file +export async function handleFetchEvent(msg: any): Promise { + try { + logger.info({ msg, event: Event.DISCORD_BOT.FETCH, sagaId: msg.content.uuid }, 'is running'); + if (msg === undefined || msg === null) return; + const { content } = msg; + const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); + // eslint-disable-next-line @typescript-eslint/return-await + await saga.next(async () => fetchMethod(msg)); + logger.info({ msg, event: Event.DISCORD_BOT.FETCH, sagaId: msg.content.uuid }, 'is done'); + } catch (error) { + logger.error({ msg, event: Event.DISCORD_BOT.FETCH, sagaId: msg.content.uuid, error }, 'is failed'); + } +} diff --git a/src/rabbitmq/events/fetchMembersEvent.ts b/src/rabbitmq/events/fetchMembersEvent.ts index 2b90b20c..5bc1dda1 100644 --- a/src/rabbitmq/events/fetchMembersEvent.ts +++ b/src/rabbitmq/events/fetchMembersEvent.ts @@ -1,5 +1,5 @@ -import { DatabaseManager, IPlatform } from '@togethercrew.dev/db'; -import { HydratedDocument } from 'mongoose'; +import { DatabaseManager, type IPlatform } from '@togethercrew.dev/db'; +import { type HydratedDocument } from 'mongoose'; import fetchMembers from '../../functions/fetchMembers'; import fetchChannels from '../../functions/fetchChannels'; import fetchRoles from '../../functions/fetchRoles'; @@ -9,7 +9,7 @@ import { platformService } from '../../database/services'; const logger = parentLogger.child({ module: `${Event.DISCORD_BOT.FETCH_MEMBERS}` }); -const fetchInitialData = async (platform: HydratedDocument) => { +const fetchInitialData = async (platform: HydratedDocument): Promise => { try { const connection = await DatabaseManager.getInstance().getTenantDb(platform.metadata?.id); await fetchChannels(connection, platform); @@ -20,25 +20,24 @@ const fetchInitialData = async (platform: HydratedDocument) => { } }; -export async function handleFetchMembersEvent(msg: any) { +export async function handleFetchMembersEvent(msg: any): Promise { try { logger.info({ msg, event: Event.DISCORD_BOT.FETCH_MEMBERS, sagaId: msg.content.uuid }, 'is running'); - if (!msg) return; + if (msg === undefined || msg === null) return; const { content } = msg; const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); - const platformId = saga.data['platformId']; + const platformId = saga.data.platformId; const platform = await platformService.getPlatform({ _id: platformId }); - if (platform) { + if (platform !== null) { const fn = fetchInitialData.bind({}, platform); await saga.next(fn); } logger.info({ msg, event: Event.DISCORD_BOT.FETCH_MEMBERS, sagaId: msg.content.uuid }, 'is done'); } catch (error) { logger.error({ msg, event: Event.DISCORD_BOT.FETCH_MEMBERS, sagaId: msg.content.uuid, error }, 'is failed'); - } -} \ No newline at end of file +} diff --git a/src/rabbitmq/events/interactionResponseCreate.ts b/src/rabbitmq/events/interactionResponseCreate.ts index 433dfdbf..8f39884b 100644 --- a/src/rabbitmq/events/interactionResponseCreate.ts +++ b/src/rabbitmq/events/interactionResponseCreate.ts @@ -1,28 +1,17 @@ import { Event } from '@togethercrew.dev/tc-messagebroker'; -import { ChatInputCommandInteraction_broker, InteractionResponse } from '../../interfaces/Hivemind.interface'; import { interactionService } from '../../services'; import parentLogger from '../../config/logger'; const logger = parentLogger.child({ module: `${Event.DISCORD_BOT.INTERACTION_RESPONSE.CREATE}` }); // eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function handleInteractionResponseCreate(msg: any) { - try { - logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.CREATE, }, 'is running'); - // const interaction: ChatInputCommandInteraction_broker = JSON.parse(msg?.content.interaction); - // const data: InteractionResponse = JSON.parse(msg?.content.data); - - - const interaction = msg?.content.interaction; - const data = msg?.content.data; - - console.log(interaction) - - console.log(data) - - await interactionService.createInteractionResponse(interaction, data); - logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.CREATE }, 'is done'); - } catch (error) { - console.log(error) - logger.error({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.CREATE, error }, 'is failed'); - } -} \ No newline at end of file +export async function handleInteractionResponseCreate(msg: any): Promise { + try { + logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.CREATE }, 'is running'); + const interaction = msg?.content.interaction; + const data = msg?.content.data; + await interactionService.createInteractionResponse(interaction, data); + logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.CREATE }, 'is done'); + } catch (error) { + logger.error({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.CREATE, error }, 'is failed'); + } +} diff --git a/src/rabbitmq/events/interactionResponseDelete.ts b/src/rabbitmq/events/interactionResponseDelete.ts index 0a64cd49..95ffb48f 100644 --- a/src/rabbitmq/events/interactionResponseDelete.ts +++ b/src/rabbitmq/events/interactionResponseDelete.ts @@ -1,19 +1,16 @@ import { Event } from '@togethercrew.dev/tc-messagebroker'; -import { ChatInputCommandInteraction_broker } from '../../interfaces/Hivemind.interface'; import { interactionService } from '../../services'; import parentLogger from '../../config/logger'; const logger = parentLogger.child({ module: `${Event.DISCORD_BOT.INTERACTION_RESPONSE.DELETE}` }); // eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function handleInteractionResponseDelete(msg: any) { - try { - logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.DELETE }, 'is running'); - // const interaction: ChatInputCommandInteraction_broker = JSON.parse(msg?.content.interaction); - - const interaction = msg?.content.interaction; - await interactionService.deleteOriginalInteractionResponse(interaction) - logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.DELETE }, 'is done'); - } catch (error) { - logger.error({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.DELETE, error }, 'is failed'); - } -} \ No newline at end of file +export async function handleInteractionResponseDelete(msg: any): Promise { + try { + logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.DELETE }, 'is running'); + const interaction = msg?.content.interaction; + await interactionService.deleteOriginalInteractionResponse(interaction); + logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.DELETE }, 'is done'); + } catch (error) { + logger.error({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.DELETE, error }, 'is failed'); + } +} diff --git a/src/rabbitmq/events/interactionResponseEdit.ts b/src/rabbitmq/events/interactionResponseEdit.ts index e8a1e136..c916d22d 100644 --- a/src/rabbitmq/events/interactionResponseEdit.ts +++ b/src/rabbitmq/events/interactionResponseEdit.ts @@ -1,22 +1,17 @@ import { Event } from '@togethercrew.dev/tc-messagebroker'; -import { ChatInputCommandInteraction_broker, InteractionResponseEditData } from '../../interfaces/Hivemind.interface'; import { interactionService } from '../../services'; import parentLogger from '../../config/logger'; const logger = parentLogger.child({ module: `${Event.DISCORD_BOT.INTERACTION_RESPONSE.EDIT}` }); // eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function handleInteractionResponseEdit(msg: any) { - try { - logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.EDIT }, 'is running'); - // const interaction: ChatInputCommandInteraction_broker = JSON.parse(msg?.content.interaction); - // const data: InteractionResponseEditData = JSON.parse(msg?.content.data); - - const interaction = msg?.content.interaction; - const data = msg?.content.data; - - await interactionService.editOriginalInteractionResponse(interaction, data) - logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.EDIT }, 'is done'); - } catch (error) { - logger.error({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.EDIT, error }, 'is failed'); - } -} \ No newline at end of file +export async function handleInteractionResponseEdit(msg: any): Promise { + try { + logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.EDIT }, 'is running'); + const interaction = msg?.content.interaction; + const data = msg?.content.data; + await interactionService.editOriginalInteractionResponse(interaction, data); + logger.info({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.EDIT }, 'is done'); + } catch (error) { + logger.error({ msg, event: Event.DISCORD_BOT.INTERACTION_RESPONSE.EDIT, error }, 'is failed'); + } +} diff --git a/src/rabbitmq/events/sendMessageEvent.ts b/src/rabbitmq/events/sendMessageEvent.ts index 3e83b14e..85fb8187 100644 --- a/src/rabbitmq/events/sendMessageEvent.ts +++ b/src/rabbitmq/events/sendMessageEvent.ts @@ -1,65 +1,67 @@ -import { Channel, ChannelType, Snowflake, TextChannel } from 'discord.js'; +/* eslint-disable @typescript-eslint/consistent-type-assertions */ +import { type Channel, ChannelType, type Snowflake, type TextChannel } from 'discord.js'; import { Event, MBConnection } from '@togethercrew.dev/tc-messagebroker'; import { coreService } from '../../services'; import { createPrivateThreadAndSendMessage } from '../../functions/thread'; import parentLogger from '../../config/logger'; import { platformService } from '../../database/services'; -import sendDirectMessage from '../../functions/sendDirectMessage'; import { addDirectMessage } from '../../queue/queues/directMessage'; const logger = parentLogger.child({ module: `${Event.DISCORD_BOT.SEND_MESSAGE}` }); - const notifyUserAboutAnalysisFinish = async ( - discordId: string, - info: { guildId: Snowflake; message: string; useFallback: boolean } -) => { - const client = await coreService.DiscordBotManager.getClient(); + discordId: string, + info: { guildId: Snowflake; message: string; useFallback: boolean }, +): Promise => { + const client = await coreService.DiscordBotManager.getClient(); - // related issue https://github.com/RnDAO/tc-discordBot/issues/68 - const { guildId, message, useFallback } = info; + // 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 guild = await client.guilds.fetch(guildId); + 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 { - addDirectMessage(discordId, message) - } catch (error) { - // 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}`, - }); - } + try { + addDirectMessage(discordId, message); + } catch (error) { + // 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) + await createPrivateThreadAndSendMessage(upperTextChannel, { + threadName: 'TogetherCrew Status', + message: `<@${discordId}> ${message}`, + }); + } }; -export async function handleSendMessageEvent(msg: any) { - try { - logger.info({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE, sagaId: msg.content.uuid }, 'is running'); - if (!msg) return; - - const { content } = msg; - const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); - const platformId = saga.data['platformId']; - const platform = await platformService.getPlatform({ _id: platformId }); const discordId = saga.data['discordId']; - const message = saga.data['message']; - const useFallback = saga.data['useFallback']; - if (platform) { - await saga.next(() => notifyUserAboutAnalysisFinish(discordId, { guildId: platform.metadata?.id, message, useFallback })); - } - - logger.info({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE, sagaId: msg.content.uuid }, 'is done'); - } catch (error) { - logger.error({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE, sagaId: msg.content.uuid, error }, 'is failed'); +export async function handleSendMessageEvent(msg: any): Promise { + try { + logger.info({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE, sagaId: msg.content.uuid }, 'is running'); + if (msg === undefined || msg === null) return; + const { content } = msg; + const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); + const platformId = saga.data.platformId; + const platform = await platformService.getPlatform({ _id: platformId }); + const discordId = saga.data.discordId; + const message = saga.data.message; + const useFallback = saga.data.useFallback; + if (platform !== null) { + await saga.next(async () => + // eslint-disable-next-line @typescript-eslint/return-await + notifyUserAboutAnalysisFinish(discordId, { guildId: platform.metadata?.id, message, useFallback }), + ); } -} \ No newline at end of file + + logger.info({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE, sagaId: msg.content.uuid }, 'is done'); + } catch (error) { + logger.error({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE, sagaId: msg.content.uuid, error }, 'is failed'); + } +} diff --git a/src/rabbitmq/events/sendMessageToChannel.ts b/src/rabbitmq/events/sendMessageToChannel.ts index 365187f2..79842213 100644 --- a/src/rabbitmq/events/sendMessageToChannel.ts +++ b/src/rabbitmq/events/sendMessageToChannel.ts @@ -1,29 +1,29 @@ import { Event, MBConnection } from '@togethercrew.dev/tc-messagebroker'; import parentLogger from '../../config/logger'; -import { channelService } from '../../services'; -import { addChannelMessage } from '../../queue/queues/channelMessage' +import { addChannelMessage } from '../../queue/queues/channelMessage'; const logger = parentLogger.child({ module: `${Event.DISCORD_BOT.SEND_MESSAGE}` }); -export async function handleSendMessageToChannel(msg: any) { - try { - logger.info({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE_TO_CHANNEL, sagaId: msg.content.uuid }, 'is running'); - if (!msg) return; +export async function handleSendMessageToChannel(msg: any): Promise { + try { + logger.info({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE_TO_CHANNEL, sagaId: msg.content.uuid }, 'is running'); + if (msg === undefined || msg === null) return; + const { content } = msg; + const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); + const channels = saga.data.channels; + const message = saga.data.message; - const { content } = msg; - const saga = await MBConnection.models.Saga.findOne({ sagaId: content.uuid }); - const channels = saga.data['channels']; - const message = saga.data['message']; + // IS THIS CORRECT WAY? + await saga.next(async () => { + for await (const channel of channels) { + addChannelMessage(channel, message); + } + }); - // IS THIS CORRECT WAY? - await saga.next(async () => { - for await (const channel of channels) { - addChannelMessage(channel, message) - } - }); - - logger.info({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE_TO_CHANNEL, sagaId: msg.content.uuid }, 'is done'); - } catch (error) { - logger.error({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE_TO_CHANNEL, sagaId: msg.content.uuid, error }, 'is failed'); - - } -} \ No newline at end of file + logger.info({ msg, event: Event.DISCORD_BOT.SEND_MESSAGE_TO_CHANNEL, sagaId: msg.content.uuid }, 'is done'); + } catch (error) { + logger.error( + { msg, event: Event.DISCORD_BOT.SEND_MESSAGE_TO_CHANNEL, sagaId: msg.content.uuid, error }, + 'is failed', + ); + } +} diff --git a/src/services/channel.service.ts b/src/services/channel.service.ts index 55959352..e7bfc2f6 100644 --- a/src/services/channel.service.ts +++ b/src/services/channel.service.ts @@ -1,19 +1,21 @@ -import { Snowflake } from 'discord.js'; +import { type Snowflake, type Message } from 'discord.js'; import coreService from './core.service'; /** - * send message to channel + * Send message to channel * @param {Snowflake} discordId - channel discordId. - * @param {string[]} message - message string. + * @param {string} message - message string. + * @returns {Promise} - The sent message or undefined if unable to send. */ -async function sendChannelMessage(discordId: Snowflake, message: string) { - const client = await coreService.DiscordBotManager.getClient(); - const channel = await client.channels.fetch(discordId); - if (channel && channel.isTextBased()) { - return await channel.send(message); - } +async function sendChannelMessage(discordId: Snowflake, message: string): Promise { + const client = await coreService.DiscordBotManager.getClient(); + const channel = await client.channels.fetch(discordId); + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/prefer-optional-chain + if (channel && channel.isTextBased()) { + return await channel.send(message); + } } export default { - sendChannelMessage -} \ No newline at end of file + sendChannelMessage, +}; diff --git a/src/services/command.service.ts b/src/services/command.service.ts index 2876e1a4..d4b7a40a 100644 --- a/src/services/command.service.ts +++ b/src/services/command.service.ts @@ -6,46 +6,45 @@ import { coreService } from '../services'; import parentLogger from '../config/logger'; const logger = parentLogger.child({ module: 'CommandService' }); -async function loadCommands() { - const client = await coreService.DiscordBotManager.getClient(); - client.commands = new Collection(); - const foldersPath: string = path.join(__dirname, '../commands'); - const commandFolders: string[] = await readdir(foldersPath); - for (const folder of commandFolders) { - const commandsPath: string = path.join(foldersPath, folder); - const commandFiles: string[] = (await readdir(commandsPath)).filter( - file => file.endsWith('.ts') || file.endsWith('.js') - ); - for (const file of commandFiles) { - const filePath: string = path.join(commandsPath, file); - const command = (await import(filePath)).default; - if ('data' in command && 'execute' in command) { - client.commands.set(command.data.name, command); - } else { - logger.warn({ commandPath: filePath }, 'The command is missing a required "data" or "execute" property.') - } - } +async function loadCommands(): Promise { + const client = await coreService.DiscordBotManager.getClient(); + client.commands = new Collection(); + const foldersPath: string = path.join(__dirname, '../commands'); + const commandFolders: string[] = await readdir(foldersPath); + for (const folder of commandFolders) { + const commandsPath: string = path.join(foldersPath, folder); + const commandFiles: string[] = (await readdir(commandsPath)).filter( + (file) => file.endsWith('.ts') || file.endsWith('.js'), + ); + for (const file of commandFiles) { + const filePath: string = path.join(commandsPath, file); + const command = (await import(filePath)).default; + if ('data' in command && 'execute' in command) { + client.commands.set(command.data.name, command); + } else { + logger.warn({ commandPath: filePath }, 'The command is missing a required "data" or "execute" property.'); + } } + } } -async function registerCommand() { - try { - const client = await coreService.DiscordBotManager.getClient(); - const rest = new REST().setToken(config.discord.botToken) - const commandData = [...client.commands.values()].map(command => command.data.toJSON()); - await rest.put( - // RnDAO: 915914985140531240 - Routes.applicationGuildCommands(config.discord.clientId, "915914985140531240"), - { body: commandData }, - ); - logger.info('Commands Registerd'); - - } catch (err) { - logger.error({ err }, 'Failed to register the slash command'); - } +async function registerCommand(): Promise { + try { + const client = await coreService.DiscordBotManager.getClient(); + const rest = new REST().setToken(config.discord.botToken); + const commandData = [...client.commands.values()].map((command) => command.data.toJSON()); + await rest.put( + // RnDAO: 915914985140531240 + Routes.applicationGuildCommands(config.discord.clientId, '915914985140531240'), + { body: commandData }, + ); + logger.info('Commands Registerd'); + } catch (err) { + logger.error({ err }, 'Failed to register the slash command'); + } } export default { - loadCommands, - registerCommand -} \ No newline at end of file + loadCommands, + registerCommand, +}; diff --git a/src/services/core.service.ts b/src/services/core.service.ts index a5b43592..45ea0f45 100644 --- a/src/services/core.service.ts +++ b/src/services/core.service.ts @@ -1,47 +1,46 @@ import { Client, GatewayIntentBits } from 'discord.js'; import config from '../config'; +// eslint-disable-next-line @typescript-eslint/no-extraneous-class class DiscordBotManager { - public static client: Client; - public static async getClient(): Promise { - if (!DiscordBotManager.client) { - DiscordBotManager.client = new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMembers, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.GuildPresences, - GatewayIntentBits.DirectMessages, - ], - }); - await DiscordBotManager.client.login(config.discord.botToken); - - } - return DiscordBotManager.client; - } - - public static async initClient(): Promise { - DiscordBotManager.client = new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMembers, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.GuildPresences, - GatewayIntentBits.DirectMessages, - ], - }); - - return DiscordBotManager.client; + public static client: Client; + public static async getClient(): Promise { + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + if (!DiscordBotManager.client) { + DiscordBotManager.client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.GuildPresences, + GatewayIntentBits.DirectMessages, + ], + }); + await DiscordBotManager.client.login(config.discord.botToken); } - - public static async LoginClient(): Promise { - await DiscordBotManager.client.login(config.discord.botToken); - return DiscordBotManager.client; - } - + return DiscordBotManager.client; + } + + public static async initClient(): Promise { + DiscordBotManager.client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.GuildPresences, + GatewayIntentBits.DirectMessages, + ], + }); + + return DiscordBotManager.client; + } + + public static async LoginClient(): Promise { + await DiscordBotManager.client.login(config.discord.botToken); + return DiscordBotManager.client; + } } - export default { - DiscordBotManager -} \ No newline at end of file + DiscordBotManager, +}; diff --git a/src/services/event.service.ts b/src/services/event.service.ts index 7b629bb4..b46b9645 100644 --- a/src/services/event.service.ts +++ b/src/services/event.service.ts @@ -2,29 +2,29 @@ import path from 'path'; import { readdir } from 'node:fs/promises'; import { coreService } from '../services'; -async function loadEvents() { - const client = await coreService.DiscordBotManager.getClient(); - const foldersPath: string = path.join(__dirname, '../events'); - 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') - ); - for (const file of eventFiles) { - const filePath: string = path.join(eventsPath, file); - const event = (await import(filePath)).default; - if (event.once) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - client.once(event.name, (...args: any[]) => event.execute(...args)); - } else { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - client.on(event.name, (...args: any[]) => event.execute(...args)); - } - } +async function loadEvents(): Promise { + const client = await coreService.DiscordBotManager.getClient(); + const foldersPath: string = path.join(__dirname, '../events'); + 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'), + ); + for (const file of eventFiles) { + const filePath: string = path.join(eventsPath, file); + const event = (await import(filePath)).default; + if (event.once === true) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + client.once(event.name, (...args: any[]) => event.execute(...args)); + } else { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + client.on(event.name, (...args: any[]) => event.execute(...args)); + } } + } } export default { - loadEvents -} \ No newline at end of file + loadEvents, +}; diff --git a/src/services/index.ts b/src/services/index.ts index 5e73a3d4..4a2259cc 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -4,11 +4,4 @@ import coreService from './core.service'; import eventService from './event.service'; import commandService from './command.service'; import interactionService from './interaction.service'; -export { - channelService, - userService, - coreService, - eventService, - commandService, - interactionService -} \ No newline at end of file +export { channelService, userService, coreService, eventService, commandService, interactionService }; diff --git a/src/services/interaction.service.ts b/src/services/interaction.service.ts index bf5e7832..548634d0 100644 --- a/src/services/interaction.service.ts +++ b/src/services/interaction.service.ts @@ -1,7 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import fetch from 'node-fetch'; import parentLogger from '../config/logger'; -import { ChatInputCommandInteraction_broker, InteractionResponse, InteractionResponseEditData } from '../interfaces/Hivemind.interface'; +import { + type ChatInputCommandInteraction_broker, + type InteractionResponse, + type InteractionResponseEditData, +} from '../interfaces/Hivemind.interface'; const logger = parentLogger.child({ module: 'InteractionResponses' }); @@ -11,27 +15,39 @@ const logger = parentLogger.child({ module: 'InteractionResponses' }); @param {string} redirect_uri * @returns {Promise} */ -async function createInteractionResponse(interaction: ChatInputCommandInteraction_broker, data: InteractionResponse) { - try { - // {4, 5, 9, 10, 11} - const { type, ...rest } = data; - const body = { - type: type, - data: rest.data - }; +async function createInteractionResponse( + interaction: ChatInputCommandInteraction_broker, + data: InteractionResponse, +): Promise { + try { + // {4, 5, 9, 10, 11} + const { type, ...rest } = data; + const body = { + type, + data: rest.data, + }; + if (interaction.token === null || interaction.token === undefined) { + throw new Error('InteractionToken is null or undefined'); + } - const response = await fetch(`https://discord.com/api/interactions/${interaction.id}/${interaction.token}/callback`, { - method: 'POST', - body: JSON.stringify(body), - headers: { 'Content-Type': 'application/json' } - }) - if (!response.ok) { - throw new Error(); - } - } catch (error) { - logger.error({ interaction_id: interaction.id, interaction_token: interaction.token, error }, 'Failed to send interaction response'); + const response = await fetch( + `https://discord.com/api/interactions/${interaction.id}/${interaction.token}/callback`, + { + method: 'POST', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' }, + }, + ); + if (!response.ok) { + throw new Error(); } + } catch (error) { + logger.error( + { interaction_id: interaction.id, interaction_token: interaction.token, error }, + 'Failed to send interaction response', + ); + } } /** @@ -40,21 +56,32 @@ async function createInteractionResponse(interaction: ChatInputCommandInteractio @param {string} redirect_uri * @returns {Promise} */ -async function getOriginalInteractionResponse(interaction: ChatInputCommandInteraction_broker) { - try { - const response = await fetch(`https://discord.com/api/webhooks/${interaction.applicationId}/${interaction.token}/messages/@original`, { - method: 'GET', - headers: { 'Content-Type': 'application/json' } - }) - if (response.ok) { - return await response.json(); - } - else { - throw new Error(await response.json()); - } - } catch (error) { - logger.error({ application_id: interaction.applicationId, interaction_token: interaction.token, error }, '100'); +async function getOriginalInteractionResponse(interaction: ChatInputCommandInteraction_broker): Promise { + try { + if ( + interaction.token === null || + interaction.token === undefined || + interaction.applicationId === null || + interaction.applicationId === undefined + ) { + throw new Error('InteractionToken or InteractionApplicationId is null or undefined'); + } + + const response = await fetch( + `https://discord.com/api/webhooks/${interaction.applicationId}/${interaction.token}/messages/@original`, + { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }, + ); + if (response.ok) { + return await response.json(); + } else { + throw new Error(await response.json()); } + } catch (error) { + logger.error({ application_id: interaction.applicationId, interaction_token: interaction.token, error }, '100'); + } } /** @@ -63,24 +90,37 @@ async function getOriginalInteractionResponse(interaction: ChatInputCommandInter @param {string} redirect_uri * @returns {Promise} */ -async function editOriginalInteractionResponse(interaction: ChatInputCommandInteraction_broker, data: InteractionResponseEditData) { - try { - const response = await fetch(`https://discord.com/api/webhooks/${interaction.applicationId}/${interaction.token}/messages/@original`, { - method: 'PATCH', - body: JSON.stringify(data), - headers: { 'Content-Type': 'application/json' } - }) - if (response.ok) { - return await response.json(); - } - else { - throw new Error(await response.json()); - } - } catch (error) { - logger.error({ application_id: interaction.applicationId, interaction_token: interaction.token, error }, '100'); +async function editOriginalInteractionResponse( + interaction: ChatInputCommandInteraction_broker, + data: InteractionResponseEditData, +): Promise { + try { + if ( + interaction.token === null || + interaction.token === undefined || + interaction.applicationId === null || + interaction.applicationId === undefined + ) { + throw new Error('InteractionToken or InteractionApplicationId is null or undefined'); } -} + const response = await fetch( + `https://discord.com/api/webhooks/${interaction.applicationId}/${interaction.token}/messages/@original`, + { + method: 'PATCH', + body: JSON.stringify(data), + headers: { 'Content-Type': 'application/json' }, + }, + ); + if (response.ok) { + return await response.json(); + } else { + throw new Error(await response.json()); + } + } catch (error) { + logger.error({ application_id: interaction.applicationId, interaction_token: interaction.token, error }, '100'); + } +} /** * exchange discord code with access token @@ -88,20 +128,31 @@ async function editOriginalInteractionResponse(interaction: ChatInputCommandInte @param {string} redirect_uri * @returns {Promise} */ -async function deleteOriginalInteractionResponse(interaction: ChatInputCommandInteraction_broker) { - try { - const response = await fetch(`https://discord.com/api/webhooks/${interaction.applicationId}/${interaction.token}/messages/@original`, { - method: 'DELETE', - // headers: { 'Content-Type': 'application/json' } - }) - if (!response.ok) { - throw new Error(await response.json()); - } - } catch (error) { - logger.error({ application_id: interaction.applicationId, interaction_token: interaction.token, error }, '100'); +async function deleteOriginalInteractionResponse(interaction: ChatInputCommandInteraction_broker): Promise { + try { + if ( + interaction.token === null || + interaction.token === undefined || + interaction.applicationId === null || + interaction.applicationId === undefined + ) { + throw new Error('InteractionToken or InteractionApplicationId is null or undefined'); } -} + const response = await fetch( + `https://discord.com/api/webhooks/${interaction.applicationId}/${interaction.token}/messages/@original`, + { + method: 'DELETE', + // headers: { 'Content-Type': 'application/json' } + }, + ); + if (!response.ok) { + throw new Error(await response.json()); + } + } catch (error) { + logger.error({ application_id: interaction.applicationId, interaction_token: interaction.token, error }, '100'); + } +} /** * exchange discord code with access token @@ -109,63 +160,75 @@ async function deleteOriginalInteractionResponse(interaction: ChatInputCommandIn @param {string} redirect_uri * @returns {Promise} */ -async function createFollowUpMessage(interaction: ChatInputCommandInteraction_broker, data: object) { - try { - const response = await fetch(`https://discord.com/api/webhooks/${interaction.applicationId}/${interaction.token}`, { - method: 'POST', - body: JSON.stringify(data), - headers: { 'Content-Type': 'application/json' } - }) - if (response.ok) { - return await response.json(); - } - else { - throw new Error(await response.json()); - } - } catch (error) { - logger.error({ interaction_id: interaction.id, interaction_token: interaction.token, error }, 'Failed to create followip message'); +async function createFollowUpMessage(interaction: ChatInputCommandInteraction_broker, data: object): Promise { + try { + if ( + interaction.token === null || + interaction.token === undefined || + interaction.applicationId === null || + interaction.applicationId === undefined + ) { + throw new Error('InteractionToken or InteractionApplicationId is null or undefined'); } -} -function constructSerializableInteraction(interaction: ChatInputCommandInteraction_broker): ChatInputCommandInteraction_broker { - return { - id: interaction.id, - applicationId: interaction.applicationId, - type: interaction.type, - guildId: interaction.guildId, - guild: interaction.guild, - channel: interaction.channel, - channelId: interaction.channelId, - token: interaction.token, - user: interaction.user, - createdAt: interaction.createdAt, - deferred: interaction.deferred, - replied: interaction.replied, - webhook: interaction.webhook, - member: interaction.member, - ephemeral: interaction.ephemeral, - createdTimestamp: interaction.createdTimestamp, - appPermissions: interaction.appPermissions as any, - memberPermissions: interaction.memberPermissions as any, - locale: interaction.locale, - guildLocale: interaction.guildLocale, - client: interaction.client, - command: interaction.command, - commandId: interaction.commandId, - commandName: interaction.commandName, - commandType: interaction.commandType, - commandGuildId: interaction.commandGuildId, - options: interaction.options, - version: interaction.version, - }; + const response = await fetch(`https://discord.com/api/webhooks/${interaction.applicationId}/${interaction.token}`, { + method: 'POST', + body: JSON.stringify(data), + headers: { 'Content-Type': 'application/json' }, + }); + if (response.ok) { + return await response.json(); + } else { + throw new Error(await response.json()); + } + } catch (error) { + logger.error( + { interaction_id: interaction.id, interaction_token: interaction.token, error }, + 'Failed to create followip message', + ); + } } +function constructSerializableInteraction( + interaction: ChatInputCommandInteraction_broker, +): ChatInputCommandInteraction_broker { + return { + id: interaction.id, + applicationId: interaction.applicationId, + type: interaction.type, + guildId: interaction.guildId, + guild: interaction.guild, + channel: interaction.channel, + channelId: interaction.channelId, + token: interaction.token, + user: interaction.user, + createdAt: interaction.createdAt, + deferred: interaction.deferred, + replied: interaction.replied, + webhook: interaction.webhook, + member: interaction.member, + ephemeral: interaction.ephemeral, + createdTimestamp: interaction.createdTimestamp, + appPermissions: interaction.appPermissions as any, + memberPermissions: interaction.memberPermissions as any, + locale: interaction.locale, + guildLocale: interaction.guildLocale, + client: interaction.client, + command: interaction.command, + commandId: interaction.commandId, + commandName: interaction.commandName, + commandType: interaction.commandType, + commandGuildId: interaction.commandGuildId, + options: interaction.options, + version: interaction.version, + }; +} export default { - createInteractionResponse, - getOriginalInteractionResponse, - editOriginalInteractionResponse, - deleteOriginalInteractionResponse, - createFollowUpMessage, - constructSerializableInteraction, -} \ No newline at end of file + createInteractionResponse, + getOriginalInteractionResponse, + editOriginalInteractionResponse, + deleteOriginalInteractionResponse, + createFollowUpMessage, + constructSerializableInteraction, +}; diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 24d1b64e..21599e27 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -1,20 +1,20 @@ -import { Snowflake } from 'discord.js'; +import { type Snowflake, type Message } from 'discord.js'; import coreService from './core.service'; /** - * send direct message to user + * Send direct message to user. * @param {Snowflake} discordId - user discordId. - * @param {string[]} message - message string. - * @returns + * @param {string} message - message string. + * @returns {Promise} A promise that resolves with the sent message or undefined. */ -async function sendDirectMessage(discordId: Snowflake, message: string) { - const client = await coreService.DiscordBotManager.getClient(); - const user = await client.users.fetch(discordId); - if (user) { - return await user.send(message); - } +async function sendDirectMessage(discordId: Snowflake, message: string): Promise { + const client = await coreService.DiscordBotManager.getClient(); + const user = await client.users.fetch(discordId); + if (user !== null && user !== undefined) { + return await user.send(message); + } } export default { - sendDirectMessage -} \ No newline at end of file + sendDirectMessage, +}; diff --git a/src/utils/obj.ts b/src/utils/obj.ts index 3db32404..c7160dd3 100644 --- a/src/utils/obj.ts +++ b/src/utils/obj.ts @@ -1,47 +1,47 @@ export function handleBigInts(obj: any, seen = new WeakSet()): any { - if (typeof obj === 'bigint') { - return obj.toString(); // Convert BigInt to a string - } else if (Array.isArray(obj)) { - return obj.map((item) => handleBigInts(item, seen)); // Process arrays element-wise - } else if (typeof obj === 'object' && obj !== null) { - if (seen.has(obj)) { - // If we've seen this object before, don't process it again - return; - } - seen.add(obj); // Mark this object as seen + if (typeof obj === 'bigint') { + return obj.toString(); // Convert BigInt to a string + } else if (Array.isArray(obj)) { + return obj.map((item) => handleBigInts(item, seen)); // Process arrays element-wise + } else if (typeof obj === 'object' && obj !== null) { + if (seen.has(obj)) { + // If we've seen this object before, don't process it again + return; + } + seen.add(obj); // Mark this object as seen - const result: any = {}; - for (const [key, value] of Object.entries(obj)) { - result[key] = handleBigInts(value, seen); // Recursively process nested objects - } - return result; + const result: any = {}; + for (const [key, value] of Object.entries(obj)) { + result[key] = handleBigInts(value, seen); // Recursively process nested objects } - return obj; // Return the value unchanged if it's neither an object nor a BigInt + return result; + } + return obj; // Return the value unchanged if it's neither an object nor a BigInt } +export function removeCircularReferences(obj: T): T { + const seenObjects = new WeakMap(); -export function removeCircularReferences(obj: any, parent = null) { - const seenObjects = new WeakMap(); + function detect(obj: any, parent: any): void { + // Explicitly check that obj is not null or undefined + if (obj !== null && obj !== undefined && typeof obj === 'object') { + if (seenObjects.has(obj)) { + return; + } + seenObjects.set(obj, true); - function detect(obj: any, parent: any) { - if (obj && typeof obj === 'object') { - if (seenObjects.has(obj)) { - return '[Circular]'; - } - seenObjects.set(obj, true); - - Object.keys(obj).forEach(key => { - if (typeof obj[key] === 'object' && obj[key] !== null) { - if (parent === obj[key]) { - obj[key] = '[Circular]'; - } else { - detect(obj[key], obj); - } - } - }); + Object.keys(obj).forEach((key) => { + if (typeof obj[key] === 'object' && obj[key] !== null) { + if (parent === obj[key]) { + obj[key] = '[Circular]'; + } else { + detect(obj[key], obj); + } } + }); } + } - detect(obj, parent); - return obj; -} \ No newline at end of file + detect(obj, null); + return obj; +} diff --git a/tsconfig.json b/tsconfig.json index f73b1323..88e65525 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,34 +1,25 @@ { - "compilerOptions": { - "module": "commonjs", - "declaration": false, - "removeComments": false, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "target": "ES2017", - "sourceMap": false, - "outDir": "./lib", - "baseUrl": "./", - "incremental": true, - "strict": true, - "noImplicitAny": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true - }, - "include": [ - "src/**/*", - "__tests__/**/*", - "types/**/*" - ], - "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": false, + "outDir": "./lib", + "baseUrl": "./", + "incremental": true, + "strict": true, + "noImplicitAny": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "include": ["src/**/*", "__tests__/**/*", "types/**/*"], + "ts-node": { + "transpileOnly": true, + "files": true + }, + "exclude": ["./coverage", "./lib", "__tests__", "jest.config.js"] +} diff --git a/types/types.d.ts b/types/types.d.ts index 5a16a7c7..b548d2b5 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -1,9 +1,9 @@ // global.d.ts -import { Collection } from 'discord.js'; +import { type Collection } from 'discord.js'; declare module 'discord.js' { - interface Client { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - commands: Collection; - } -} \ No newline at end of file + interface Client { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + commands: Collection; + } +}