diff --git a/src/contents/contents.controller.ts b/src/contents/contents.controller.ts index 02ba2c2..dfbff5a 100644 --- a/src/contents/contents.controller.ts +++ b/src/contents/contents.controller.ts @@ -335,7 +335,7 @@ export class TestController { async autoCategorize( @Body() autoCategorizeBody: AutoCategorizeBodyDto, ): Promise { - return this.categoryService.autoCategorize(autoCategorizeBody); + return this.categoryService.autoCategorizeForTest(autoCategorizeBody); } } @@ -459,4 +459,19 @@ export class CategoryController { ): Promise { return this.categoryService.loadFrequentCategories(user); } + + @ApiOperation({ + summary: '아티클 카테고리 자동 지정', + description: + '아티클에 적절한 카테고리를 유저의 카테고리 목록에서 찾는 메서드', + }) + @ApiBearerAuth('Authorization') + @UseGuards(JwtAuthGuard) + @Get('auto-categorize') + async autoCategorize( + @AuthUser() user: User, + @Query('link') link: string, + ): Promise { + return this.categoryService.autoCategorize(user, link); + } } diff --git a/src/contents/contents.service.ts b/src/contents/contents.service.ts index ade637f..0c61ae4 100644 --- a/src/contents/contents.service.ts +++ b/src/contents/contents.service.ts @@ -493,7 +493,8 @@ export class CategoryService { // check if category exists in user's categories(check if category name is duplicated in same level too) const category = userInDb.categories?.find( (category) => - category.slug === categorySlug && category.parentId === parentId, + category.slug === categorySlug && + (category.parentId === parentId || (!parentId && !category.parentId)), ); // if category doesn't exist, create it @@ -806,6 +807,76 @@ export class CategoryService { } async autoCategorize( + user: User, + link: string, + ): Promise { + try { + const userInDb = await this.userRepository.findOneWithCategories(user.id); + if (!userInDb) { + throw new NotFoundException('User not found'); + } + + if (!userInDb.categories) { + throw new NotFoundException('Categories not found'); + } + const categories: string[] = []; + userInDb.categories.forEach((category) => { + if (!category.parentId) { + categories.push(category.name); + } + }); + const { title, siteName, description } = + await this.contentUtil.getLinkInfo(link); + + const content = await this.contentUtil.getLinkContent(link); + + let questionLines = [ + "You are now auto categorizing machine. You can only answer a single category name or None. Here is the article's information:", + ]; + + if (title) { + questionLines.push(`The title is "${title.trim()}"`); + } + + if (content) { + const contentLength = content.length / 2; + questionLines.push( + `The opening 150 characters of the article read, "${content + .replace(/\s/g, '') + .slice(contentLength - 150, contentLength + 150) + .trim()}"`, + ); + } + + if (description) { + questionLines.push(`The description is ${description.trim()}"`); + } + + if (siteName) { + questionLines.push(`The site's name is "${siteName.trim()}"`); + } + + // Add the category options to the end of the list + questionLines.push( + `Please tell me the most appropriate category among the following. If none are suitable, return None. Here is Category options: [${categories.join( + ', ', + )}]`, + ); + + // Join all lines together into a single string + const question = questionLines.join(' '); + + const response = await this.openaiService.createChatCompletion({ + question, + }); + + return { category: response.choices[0].message?.content || 'None' }; + } catch (e) { + throw e; + } + } + + async autoCategorizeForTest( autoCategorizeBody: AutoCategorizeBodyDto, ): Promise { try {