From a24b0ec040ce51355c1ce3029643cbaad36510aa Mon Sep 17 00:00:00 2001 From: Radek <104318242+radekm2000@users.noreply.github.com> Date: Sat, 1 Jun 2024 21:03:13 +0200 Subject: [PATCH] add guild service --- server/ecommerce/src/app.module.ts | 2 + .../src/discord-bot/discord-bot.module.ts | 5 +- .../src/discord-bot/discord-bot.service.ts | 14 +++- .../src/discord-bot/src/commands/inventory.ts | 5 +- .../src/commands/remove-product.ts | 5 +- .../src/discord-bot/src/discordbot.ts | 1 - .../src/discord-guild/discord-guild.module.ts | 54 ++++++++++++ .../discord-guild/discord-guild.service.ts | 82 +++++++++++++++++++ .../product-notification.module.ts | 5 +- .../product-notification.service.ts | 6 +- .../src/products/products.controller.ts | 4 +- .../ecommerce/src/products/products.module.ts | 7 +- .../src/products/products.service.ts | 4 +- server/ecommerce/{ => src}/spi/products.ts | 0 .../src/tests/products/products.spec.ts | 3 +- 15 files changed, 177 insertions(+), 20 deletions(-) create mode 100644 server/ecommerce/src/discord-guild/discord-guild.module.ts create mode 100644 server/ecommerce/src/discord-guild/discord-guild.service.ts rename server/ecommerce/{ => src}/spi/products.ts (100%) diff --git a/server/ecommerce/src/app.module.ts b/server/ecommerce/src/app.module.ts index 5fa5531..d307696 100644 --- a/server/ecommerce/src/app.module.ts +++ b/server/ecommerce/src/app.module.ts @@ -20,6 +20,7 @@ import { FeedbacksModule } from './feedbacks/feedbacks.module'; import { DiscordBotModule } from './discord-bot/discord-bot.module'; import { DiscordNotificationsModule } from './discord-notifications/discord-notifications.module'; import { ItemNotifierModule } from './discord-bot/src/commands/notifiers/item-notifier.module'; +import { DiscordGuildModule } from './discord-guild/discord-guild.module'; @Module({ imports: [ @@ -41,6 +42,7 @@ import { ItemNotifierModule } from './discord-bot/src/commands/notifiers/item-no DiscordBotModule, DiscordNotificationsModule, ItemNotifierModule, + DiscordGuildModule, ], controllers: [AppController], providers: [AppService], diff --git a/server/ecommerce/src/discord-bot/discord-bot.module.ts b/server/ecommerce/src/discord-bot/discord-bot.module.ts index 6b578f4..723a99d 100644 --- a/server/ecommerce/src/discord-bot/discord-bot.module.ts +++ b/server/ecommerce/src/discord-bot/discord-bot.module.ts @@ -15,13 +15,16 @@ import { ProductNotification } from 'src/utils/entities/product-notification.ent import { FollowersService } from 'src/followers/followers.service'; import { ItemNotifierService } from './src/commands/notifiers/item-notifier.service'; import { DiscordNotificationsService } from 'src/discord-notifications/discord-notifications.service'; +import { IProductsService } from 'src/spi/products'; +import { DiscordGuildModule } from 'src/discord-guild/discord-guild.module'; +import { DiscordGuildService } from 'src/discord-guild/discord-guild.service'; @Module({ controllers: [DiscordBotController], providers: [ DiscordBotService, UsersService, - ProductsService, + { provide: IProductsService, useClass: ProductsService }, ProductNotificationService, FollowersService, ItemNotifierService, diff --git a/server/ecommerce/src/discord-bot/discord-bot.service.ts b/server/ecommerce/src/discord-bot/discord-bot.service.ts index b0da469..889b4f3 100644 --- a/server/ecommerce/src/discord-bot/discord-bot.service.ts +++ b/server/ecommerce/src/discord-bot/discord-bot.service.ts @@ -1,4 +1,9 @@ -import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; +import { + Inject, + Injectable, + OnModuleDestroy, + OnModuleInit, +} from '@nestjs/common'; import { Client, IntentsBitField } from 'discord.js'; import { DiscordBot } from './src/discordbot'; import 'dotenv/config'; @@ -13,17 +18,18 @@ import { TrackedUsersCommand } from './src/commands/trackers/tracked-users'; import { FollowersService } from 'src/followers/followers.service'; import { StartTrackingCommand } from './src/commands/trackers/start-tracking'; import { StopTrackingCommand } from './src/commands/trackers/stop-tracking'; +import { IProductsService } from 'src/spi/products'; @Injectable() export class DiscordBotService implements OnModuleInit { public discordBot: DiscordBot; - private readonly bot: Client; - private readonly botToken: string; + public readonly bot: Client; + public readonly botToken: string; private readonly botApplicationId: string; constructor( private userService: UsersService, - private productsService: ProductsService, + @Inject(IProductsService) private productsService: IProductsService, private followersService: FollowersService, ) { this.botToken = process.env.DISCORD_BOT_TOKEN; diff --git a/server/ecommerce/src/discord-bot/src/commands/inventory.ts b/server/ecommerce/src/discord-bot/src/commands/inventory.ts index f2690fa..e253baf 100644 --- a/server/ecommerce/src/discord-bot/src/commands/inventory.ts +++ b/server/ecommerce/src/discord-bot/src/commands/inventory.ts @@ -10,15 +10,16 @@ import { UsersService } from 'src/users/users.service'; import { ProductsService } from 'src/products/products.service'; import { Product } from 'src/utils/entities/product.entity'; import { DiscordEmbedColors } from '../discord-embeds/colors'; +import { IProductsService } from 'src/spi/products'; type Config = { usersService: UsersService; - productsService: ProductsService; + productsService: IProductsService; }; export class InventoryCommand implements SlashCommand { private readonly usersService: UsersService; - private readonly productsService: ProductsService; + private readonly productsService: IProductsService; constructor(config: Config) { this.usersService = config.usersService; diff --git a/server/ecommerce/src/discord-bot/src/commands/remove-product.ts b/server/ecommerce/src/discord-bot/src/commands/remove-product.ts index 897c4a3..553c5dd 100644 --- a/server/ecommerce/src/discord-bot/src/commands/remove-product.ts +++ b/server/ecommerce/src/discord-bot/src/commands/remove-product.ts @@ -6,12 +6,13 @@ import { import { SlashCommand } from './slash-command'; import { UsersService } from 'src/users/users.service'; import { ProductsService } from 'src/products/products.service'; +import { IProductsService } from 'src/spi/products'; const PRODUCT_OPTION_NAME = 'product'; type Config = { usersService: UsersService; - productService: ProductsService; + productService: IProductsService; }; export class RemoveProduct implements SlashCommand { @@ -27,7 +28,7 @@ export class RemoveProduct implements SlashCommand { ); private readonly usersService: UsersService; - private readonly productService: ProductsService; + private readonly productService: IProductsService; constructor(config: Config) { this.usersService = config.usersService; diff --git a/server/ecommerce/src/discord-bot/src/discordbot.ts b/server/ecommerce/src/discord-bot/src/discordbot.ts index 506fc1e..f9442a8 100644 --- a/server/ecommerce/src/discord-bot/src/discordbot.ts +++ b/server/ecommerce/src/discord-bot/src/discordbot.ts @@ -61,7 +61,6 @@ export class DiscordBot { private handleCommand = async (interaction: ChatInputCommandInteraction) => { const command = this.commands.get(interaction.commandName); if (!command) return; - try { await command.execute(interaction); } catch (error) { diff --git a/server/ecommerce/src/discord-guild/discord-guild.module.ts b/server/ecommerce/src/discord-guild/discord-guild.module.ts new file mode 100644 index 0000000..3035033 --- /dev/null +++ b/server/ecommerce/src/discord-guild/discord-guild.module.ts @@ -0,0 +1,54 @@ +import { Module } from '@nestjs/common'; +import { DiscordGuildService } from './discord-guild.service'; +import { DiscordBotService } from 'src/discord-bot/discord-bot.service'; +import { UsersService } from 'src/users/users.service'; +import { IProductsService } from 'src/spi/products'; +import { ProductsService } from 'src/products/products.service'; +import { FollowersService } from 'src/followers/followers.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Follow } from 'src/utils/entities/followers.entity'; +import { User } from 'src/utils/entities/user.entity'; +import { Profile } from 'src/utils/entities/profile.entity'; +import { Avatar } from 'src/utils/entities/avatar.entity'; +import { Image } from 'src/utils/entities/image.entity'; +import { ProductNotification } from 'src/utils/entities/product-notification.entity'; +import { Product } from 'src/utils/entities/product.entity'; +import { ProductNotificationService } from 'src/product-notification/product-notification.service'; +import { ItemNotifierService } from 'src/discord-bot/src/commands/notifiers/item-notifier.service'; +import { DiscordNotificationsService } from 'src/discord-notifications/discord-notifications.service'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ + Follow, + User, + Profile, + Avatar, + Image, + ProductNotification, + Product, + ]), + ], + providers: [ + DiscordBotService, + UsersService, + ProductNotificationService, + ItemNotifierService, + DiscordNotificationsService, + { + provide: IProductsService, + useClass: ProductsService, + }, + FollowersService, + { + provide: DiscordGuildService, + useFactory: (botService: DiscordBotService) => + new DiscordGuildService({ + botClient: botService.bot, + }), + inject: [DiscordBotService], + }, + ], + exports: [DiscordGuildService], +}) +export class DiscordGuildModule {} diff --git a/server/ecommerce/src/discord-guild/discord-guild.service.ts b/server/ecommerce/src/discord-guild/discord-guild.service.ts new file mode 100644 index 0000000..30abc93 --- /dev/null +++ b/server/ecommerce/src/discord-guild/discord-guild.service.ts @@ -0,0 +1,82 @@ +import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common'; +import { Client, MessageCreateOptions } from 'discord.js'; +import 'dotenv/config'; + +type Config = { + botClient: Client; +}; + +@Injectable() +export class DiscordGuildService { + private readonly botClient: Client; + private readonly guildId: string; + private readonly logger: Logger; + + constructor(config: Config) { + this.botClient = config.botClient; + this.logger = new Logger(DiscordGuildService.name); + this.guildId = process.env.GUILD_ID ?? ''; + } + + public assignRoles = async (userId: string, roleIds: string[]) => { + if (roleIds.length === 0) { + return; + } + + roleIds = roleIds.filter((roleId) => !this.hasRole(userId, roleId)); + + const member = await this.getGuildMember(userId); + try { + await member.roles.add(roleIds); + } catch (error) { + this.logger.error( + `Error while trying to assign roles for user ${userId}`, + ); + } + }; + + private hasRole = async (userId: string, roleId: string) => { + try { + const member = await this.getGuildMember(userId); + + return !!member && member.roles.cache.some((role) => role.id === roleId); + } catch (error) { + this.logger.error(`Error while getting roles for user ${userId}`); + } + }; + + private getGuildMember = async (userId: string) => { + const guild = await this.getGuild(); + + try { + return ( + guild.members.cache.get(userId) ?? (await guild.members.fetch(userId)) + ); + } catch (error) { + this.logger.error('Error when getting guild member'); + } + }; + + private getGuild = async () => { + try { + return ( + this.botClient.guilds.cache.get(this.guildId) ?? + (await this.botClient.guilds.fetch(this.guildId)) + ); + } catch (error) { + this.logger.error(`Error when getting guild ${this.guildId}`); + return undefined; + } + }; + + public sendMessageToMember = async ( + userId: string, + message: MessageCreateOptions, + ) => { + try { + await this.botClient.users.send(userId, message); + } catch (error) { + this.logger.error(`Error when trying to send message to ${userId}`); + } + }; +} diff --git a/server/ecommerce/src/product-notification/product-notification.module.ts b/server/ecommerce/src/product-notification/product-notification.module.ts index 44b9a89..4859ea7 100644 --- a/server/ecommerce/src/product-notification/product-notification.module.ts +++ b/server/ecommerce/src/product-notification/product-notification.module.ts @@ -14,6 +14,9 @@ import { Image } from 'src/utils/entities/image.entity'; import { ItemNotifier } from 'src/discord-bot/src/commands/notifiers/item-notifier'; import { ItemNotifierService } from 'src/discord-bot/src/commands/notifiers/item-notifier.service'; import { DiscordNotificationsService } from 'src/discord-notifications/discord-notifications.service'; +import { IProductsService } from 'src/spi/products'; +import { DiscordGuildService } from 'src/discord-guild/discord-guild.service'; +import { DiscordGuildModule } from 'src/discord-guild/discord-guild.module'; @Module({ imports: [ @@ -31,7 +34,7 @@ import { DiscordNotificationsService } from 'src/discord-notifications/discord-n providers: [ ProductNotificationService, UsersService, - ProductsService, + { provide: IProductsService, useClass: ProductsService }, ItemNotifierService, DiscordNotificationsService, ], diff --git a/server/ecommerce/src/product-notification/product-notification.service.ts b/server/ecommerce/src/product-notification/product-notification.service.ts index 721e669..39db36c 100644 --- a/server/ecommerce/src/product-notification/product-notification.service.ts +++ b/server/ecommerce/src/product-notification/product-notification.service.ts @@ -6,7 +6,7 @@ import { forwardRef, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { ProductsService } from 'src/products/products.service'; +import { IProductsService } from 'src/spi/products'; import { UsersService } from 'src/users/users.service'; import { ProductNotification } from 'src/utils/entities/product-notification.entity'; import { Product } from 'src/utils/entities/product.entity'; @@ -18,8 +18,8 @@ export class ProductNotificationService { @InjectRepository(ProductNotification) private productNotificationRepository: Repository, private usersService: UsersService, - @Inject(forwardRef(() => ProductsService)) - private productsService: ProductsService, + @Inject(forwardRef(() => IProductsService)) + private productsService: IProductsService, ) {} async notifyFollowersAboutNewProduct(product: Product) { const existingProductWithImage = await this.productsService.findProduct( diff --git a/server/ecommerce/src/products/products.controller.ts b/server/ecommerce/src/products/products.controller.ts index c3ea47a..383edc3 100644 --- a/server/ecommerce/src/products/products.controller.ts +++ b/server/ecommerce/src/products/products.controller.ts @@ -3,6 +3,7 @@ import { Controller, Delete, Get, + Inject, Param, ParseIntPipe, Post, @@ -29,11 +30,12 @@ import { ZodValidationPipe } from 'src/utils/pipes/ZodValidationPipe'; import { Response } from 'express'; import { StripeService } from 'src/stripe/stripe.service'; import { ParseAndValidateProductPipe } from 'src/utils/pipes/ParseAndValidateProductPipe'; +import { IProductsService } from 'src/spi/products'; @Controller('products') export class ProductsController { constructor( - private productsService: ProductsService, + @Inject(IProductsService) private productsService: IProductsService, private stripeService: StripeService, ) {} diff --git a/server/ecommerce/src/products/products.module.ts b/server/ecommerce/src/products/products.module.ts index c1e457c..eabd17f 100644 --- a/server/ecommerce/src/products/products.module.ts +++ b/server/ecommerce/src/products/products.module.ts @@ -17,11 +17,14 @@ import { NodemailerService } from 'src/nodemailer/nodemailer.service'; import { ItemNotifier } from 'src/discord-bot/src/commands/notifiers/item-notifier'; import { DiscordNotificationsService } from 'src/discord-notifications/discord-notifications.service'; import { ItemNotifierService } from 'src/discord-bot/src/commands/notifiers/item-notifier.service'; +import { IProductsService } from 'src/spi/products'; +import { DiscordGuildService } from 'src/discord-guild/discord-guild.service'; +import { DiscordGuildModule } from 'src/discord-guild/discord-guild.module'; @Module({ controllers: [ProductsController], providers: [ - ProductsService, + { provide: IProductsService, useClass: ProductsService }, UsersService, ProductNotificationService, StripeService, @@ -41,6 +44,6 @@ import { ItemNotifierService } from 'src/discord-bot/src/commands/notifiers/item ]), FollowersModule, ], - exports: [ProductsService], + exports: [IProductsService], }) export class ProductsModule {} diff --git a/server/ecommerce/src/products/products.service.ts b/server/ecommerce/src/products/products.service.ts index 014a5ad..3140a48 100644 --- a/server/ecommerce/src/products/products.service.ts +++ b/server/ecommerce/src/products/products.service.ts @@ -20,6 +20,8 @@ import { UsersService } from 'src/users/users.service'; import 'dotenv/config'; import { ItemNotifierService } from 'src/discord-bot/src/commands/notifiers/item-notifier.service'; import { User } from 'src/utils/entities/user.entity'; +import { IProductsService } from 'src/spi/products'; +import { DiscordGuildService } from 'src/discord-guild/discord-guild.service'; const s3 = new S3Client({ region: process.env.BUCKET_REGION, @@ -29,7 +31,7 @@ const s3 = new S3Client({ }, }); @Injectable() -export class ProductsService { +export class ProductsService implements IProductsService { logger: Logger; constructor( diff --git a/server/ecommerce/spi/products.ts b/server/ecommerce/src/spi/products.ts similarity index 100% rename from server/ecommerce/spi/products.ts rename to server/ecommerce/src/spi/products.ts diff --git a/server/ecommerce/src/tests/products/products.spec.ts b/server/ecommerce/src/tests/products/products.spec.ts index 4935f04..bdcec99 100644 --- a/server/ecommerce/src/tests/products/products.spec.ts +++ b/server/ecommerce/src/tests/products/products.spec.ts @@ -1,8 +1,7 @@ import { ProductsService } from 'src/products/products.service'; -import { Product } from 'src/utils/entities/product.entity'; describe('ProductsService getMenFilteredProducts method', () => { - let productsService: ProductsService; + let productsService: any; let productRepository: any; let productNotificationService: any; let imageRepository: any;