diff --git a/server/ecommerce/package-lock.json b/server/ecommerce/package-lock.json index ab0df23..2d9156a 100644 --- a/server/ecommerce/package-lock.json +++ b/server/ecommerce/package-lock.json @@ -13,6 +13,7 @@ "@aws-sdk/s3-request-presigner": "^3.485.0", "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", + "@nestjs/event-emitter": "^2.0.4", "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", @@ -2707,6 +2708,18 @@ } } }, + "node_modules/@nestjs/event-emitter": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-2.0.4.tgz", + "integrity": "sha512-quMiw8yOwoSul0pp3mOonGz8EyXWHSBTqBy8B0TbYYgpnG1Ix2wGUnuTksLWaaBiiOTDhciaZ41Y5fJZsSJE1Q==", + "dependencies": { + "eventemitter2": "6.4.9" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, "node_modules/@nestjs/jwt": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", @@ -6680,6 +6693,11 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", diff --git a/server/ecommerce/package.json b/server/ecommerce/package.json index 3ea386c..018b684 100644 --- a/server/ecommerce/package.json +++ b/server/ecommerce/package.json @@ -30,6 +30,7 @@ "@aws-sdk/s3-request-presigner": "^3.485.0", "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", + "@nestjs/event-emitter": "^2.0.4", "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.0.0", diff --git a/server/ecommerce/src/app.module.ts b/server/ecommerce/src/app.module.ts index d307696..705dd00 100644 --- a/server/ecommerce/src/app.module.ts +++ b/server/ecommerce/src/app.module.ts @@ -21,6 +21,7 @@ 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'; +import { EventEmitterModule } from '@nestjs/event-emitter'; @Module({ imports: [ @@ -43,6 +44,7 @@ import { DiscordGuildModule } from './discord-guild/discord-guild.module'; DiscordNotificationsModule, ItemNotifierModule, DiscordGuildModule, + EventEmitterModule.forRoot(), ], 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 723a99d..906c401 100644 --- a/server/ecommerce/src/discord-bot/discord-bot.module.ts +++ b/server/ecommerce/src/discord-bot/discord-bot.module.ts @@ -29,7 +29,17 @@ import { DiscordGuildService } from 'src/discord-guild/discord-guild.service'; FollowersService, ItemNotifierService, DiscordNotificationsService, + { + provide: DiscordGuildService, + useFactory: (botService: DiscordBotService, usersService: UsersService) => + new DiscordGuildService({ + botClient: botService.bot, + usersService: usersService, + }), + inject: [DiscordBotService, UsersService], + }, ], + imports: [ TypeOrmModule.forFeature([ Follow, diff --git a/server/ecommerce/src/discord-guild/discord-guild.module.ts b/server/ecommerce/src/discord-guild/discord-guild.module.ts index 3035033..f65c2b2 100644 --- a/server/ecommerce/src/discord-guild/discord-guild.module.ts +++ b/server/ecommerce/src/discord-guild/discord-guild.module.ts @@ -30,7 +30,6 @@ import { DiscordNotificationsService } from 'src/discord-notifications/discord-n ]), ], providers: [ - DiscordBotService, UsersService, ProductNotificationService, ItemNotifierService, @@ -39,15 +38,18 @@ import { DiscordNotificationsService } from 'src/discord-notifications/discord-n provide: IProductsService, useClass: ProductsService, }, - FollowersService, { provide: DiscordGuildService, - useFactory: (botService: DiscordBotService) => + useFactory: (botService: DiscordBotService, usersService: UsersService) => new DiscordGuildService({ botClient: botService.bot, + usersService: usersService, }), - inject: [DiscordBotService], + inject: [DiscordBotService, UsersService], }, + DiscordBotService, + + FollowersService, ], exports: [DiscordGuildService], }) diff --git a/server/ecommerce/src/discord-guild/discord-guild.service.ts b/server/ecommerce/src/discord-guild/discord-guild.service.ts index 30abc93..1bfe917 100644 --- a/server/ecommerce/src/discord-guild/discord-guild.service.ts +++ b/server/ecommerce/src/discord-guild/discord-guild.service.ts @@ -1,9 +1,17 @@ import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common'; +import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { Client, MessageCreateOptions } from 'discord.js'; import 'dotenv/config'; +import { UsersService } from 'src/users/users.service'; + +type AssignDiscordRoleEvent = { + userId: number; + VENDOR_ROLE_ID: string; +}; type Config = { botClient: Client; + usersService: UsersService; }; @Injectable() @@ -11,30 +19,60 @@ export class DiscordGuildService { private readonly botClient: Client; private readonly guildId: string; private readonly logger: Logger; + private readonly usersService: UsersService; constructor(config: Config) { + this.usersService = config.usersService; 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) { + @OnEvent('assignDiscordRole', { async: true }) + public async handleAssignDiscordRoleEvent(payload: AssignDiscordRoleEvent) { + const user = await this.getUser(payload.userId); + if (!user.discordId) { return; } - roleIds = roleIds.filter((roleId) => !this.hasRole(userId, roleId)); + if (user.products.length < 1) { + return; + } + return this.assignRoles(user.discordId, payload.VENDOR_ROLE_ID); + } + + public assignRoles = async (userId: string, roleId: string) => { + if (!roleId) { + return; + } const member = await this.getGuildMember(userId); try { - await member.roles.add(roleIds); + if (member && !(await this.hasRole(userId, roleId))) + await member.roles.add(roleId); + this.logger.log(`User ${userId} has been granted a new role`); } catch (error) { + console.log(error); this.logger.error( `Error while trying to assign roles for user ${userId}`, ); } }; + private checkUserRoles = async (userId: string, roleId: string) => { + const member = await this.getGuildMember(userId); + if (member.roles.cache.some((role) => role.id === roleId)) { + console.log('user has that role'); + } else { + console.log('user doesnt have that role'); + } + }; + + private getUser = async (userId: number) => { + const user = await this.usersService.findUserById(userId); + return user; + }; + private hasRole = async (userId: string, roleId: string) => { try { const member = await this.getGuildMember(userId); diff --git a/server/ecommerce/src/product-notification/product-notification.module.ts b/server/ecommerce/src/product-notification/product-notification.module.ts index 4859ea7..1f21a45 100644 --- a/server/ecommerce/src/product-notification/product-notification.module.ts +++ b/server/ecommerce/src/product-notification/product-notification.module.ts @@ -17,6 +17,9 @@ import { DiscordNotificationsService } from 'src/discord-notifications/discord-n import { IProductsService } from 'src/spi/products'; import { DiscordGuildService } from 'src/discord-guild/discord-guild.service'; import { DiscordGuildModule } from 'src/discord-guild/discord-guild.module'; +import { IDiscordGuildService } from 'src/spi/discord-guild'; +import { DiscordBotService } from 'src/discord-bot/discord-bot.service'; +import { FollowersService } from 'src/followers/followers.service'; @Module({ imports: [ @@ -37,6 +40,17 @@ import { DiscordGuildModule } from 'src/discord-guild/discord-guild.module'; { provide: IProductsService, useClass: ProductsService }, ItemNotifierService, DiscordNotificationsService, + { + provide: DiscordGuildService, + useFactory: (botService: DiscordBotService, usersService: UsersService) => + new DiscordGuildService({ + botClient: botService.bot, + usersService: usersService, + }), + inject: [DiscordBotService, UsersService], + }, + DiscordBotService, + FollowersService, ], exports: [ProductNotificationService], }) diff --git a/server/ecommerce/src/products/products.controller.ts b/server/ecommerce/src/products/products.controller.ts index 383edc3..f31b863 100644 --- a/server/ecommerce/src/products/products.controller.ts +++ b/server/ecommerce/src/products/products.controller.ts @@ -31,11 +31,19 @@ import { Response } from 'express'; import { StripeService } from 'src/stripe/stripe.service'; import { ParseAndValidateProductPipe } from 'src/utils/pipes/ParseAndValidateProductPipe'; import { IProductsService } from 'src/spi/products'; +import { DiscordGuildService } from 'src/discord-guild/discord-guild.service'; +import { IDiscordGuildService } from 'src/spi/discord-guild'; +import 'dotenv/config'; +import { EventEmitter2 } from '@nestjs/event-emitter'; + +const VENDOR_ROLE_ID = process.env.VENDOR_ROLE_ID ?? ''; @Controller('products') export class ProductsController { constructor( @Inject(IProductsService) private productsService: IProductsService, + private eventEmitter: EventEmitter2, + private stripeService: StripeService, ) {} @@ -117,6 +125,11 @@ export class ProductsController { @UploadedFile() file: Express.Multer.File, @AuthUser() authUser: AuthUser, ) { - return await this.productsService.uploadProduct(body, file, authUser.sub); + const userId = authUser.sub; + await this.productsService.uploadProduct(body, file, authUser.sub); + return this.eventEmitter.emit('assignDiscordRole', { + userId, + VENDOR_ROLE_ID, + }); } } diff --git a/server/ecommerce/src/products/products.module.ts b/server/ecommerce/src/products/products.module.ts index eabd17f..a59b889 100644 --- a/server/ecommerce/src/products/products.module.ts +++ b/server/ecommerce/src/products/products.module.ts @@ -14,18 +14,16 @@ import { ProductNotificationService } from 'src/product-notification/product-not import { ProductNotification } from 'src/utils/entities/product-notification.entity'; import { StripeService } from 'src/stripe/stripe.service'; 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: [ { provide: IProductsService, useClass: ProductsService }, UsersService, + ProductNotificationService, StripeService, NodemailerService, diff --git a/server/ecommerce/src/products/products.service.ts b/server/ecommerce/src/products/products.service.ts index 3140a48..6e0eb65 100644 --- a/server/ecommerce/src/products/products.service.ts +++ b/server/ecommerce/src/products/products.service.ts @@ -40,6 +40,7 @@ export class ProductsService implements IProductsService { @InjectRepository(Image) private readonly imageRepository: Repository, private usersService: UsersService, + private itemNotifierService: ItemNotifierService, ) { this.logger = new Logger(ProductsService.name); @@ -238,6 +239,7 @@ export class ProductsService implements IProductsService { this.logger.log( `User ${existingUser.username} added product with id ${newProduct.id}`, ); + return; } diff --git a/server/ecommerce/src/spi/discord-guild.ts b/server/ecommerce/src/spi/discord-guild.ts new file mode 100644 index 0000000..9dd1fd3 --- /dev/null +++ b/server/ecommerce/src/spi/discord-guild.ts @@ -0,0 +1,11 @@ +import { MessageCreateOptions } from 'discord.js'; + +export const IDiscordGuildService = Symbol('DiscordGuildService'); + +export interface IDiscordGuildService { + assignRoles(userId: string, roleIds: string[]): Promise; + sendMessageToMember( + userId: string, + message: MessageCreateOptions, + ): Promise; +} diff --git a/server/ecommerce/src/users/users.service.ts b/server/ecommerce/src/users/users.service.ts index d5f5ceb..07673fc 100644 --- a/server/ecommerce/src/users/users.service.ts +++ b/server/ecommerce/src/users/users.service.ts @@ -142,6 +142,7 @@ export class UsersService { where: { id: userId, }, + relations: ['products'], }); return user;