From d87d83d89b34b7b15bb6a9369a0019787547942f Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 7 Nov 2024 20:04:48 +0700 Subject: [PATCH] feat: plugs --- .../helpers/src/decorators/plug.decorator.ts | 2 +- .../integrations/integration.repository.ts | 37 +++++- .../integrations/integration.service.ts | 10 +- .../src/database/prisma/schema.prisma | 11 ++ .../social/linkedin.page.provider.ts | 120 +++++++++++++++--- 5 files changed, 157 insertions(+), 23 deletions(-) diff --git a/libraries/helpers/src/decorators/plug.decorator.ts b/libraries/helpers/src/decorators/plug.decorator.ts index 78900344..a16a02da 100644 --- a/libraries/helpers/src/decorators/plug.decorator.ts +++ b/libraries/helpers/src/decorators/plug.decorator.ts @@ -12,7 +12,7 @@ export function Plug(params: { validation?: RegExp; }[]; }) { - return function (target: Object, propertyKey: string | symbol) { + return function (target: Object, propertyKey: string | symbol, descriptor: any) { // Retrieve existing metadata or initialize an empty array const existingMetadata = Reflect.getMetadata('custom:plug', target) || []; diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts index ab8e4c93..845d0b10 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts @@ -13,7 +13,8 @@ export class IntegrationRepository { constructor( private _integration: PrismaRepository<'integration'>, private _posts: PrismaRepository<'post'>, - private _plugs: PrismaRepository<'plugs'> + private _plugs: PrismaRepository<'plugs'>, + private _exisingPlugData: PrismaRepository<'exisingPlugData'> ) {} async setTimes(org: string, id: string, times: IntegrationTimeDto) { @@ -342,8 +343,8 @@ export class IntegrationRepository { data: JSON.stringify(body.fields), }, select: { - activated: true - } + activated: true, + }, }); } @@ -358,4 +359,34 @@ export class IntegrationRepository { }, }); } + + async loadExisingData( + methodName: string, + integrationId: string, + id: string[] + ) { + return this._exisingPlugData.model.exisingPlugData.findMany({ + where: { + integrationId, + methodName, + value: { + in: id, + }, + }, + }); + } + + async saveExisingData( + methodName: string, + integrationId: string, + value: string[] + ) { + return this._exisingPlugData.model.exisingPlugData.createMany({ + data: value.map((p) => ({ + integrationId, + methodName, + value: p, + })), + }); + } } diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 84e42bda..c3f9e473 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -18,6 +18,7 @@ import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/ import { UploadFactory } from '@gitroom/nestjs-libraries/upload/upload.factory'; import { PlugDto } from '@gitroom/nestjs-libraries/dtos/plugs/plug.dto'; import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; +import { difference } from 'lodash'; @Injectable() export class IntegrationService { @@ -467,6 +468,12 @@ export class IntegrationService { return { id }; } + async loadExisingData (methodName: string, integrationId: string, id: string[]) { + const exisingData = await this._integrationRepository.loadExisingData(methodName, integrationId, id); + const loadOnlyIds = exisingData.map(p => p.value); + return difference(id, loadOnlyIds); + } + async startPlug(data: { orgId: string; integrationId: string; @@ -503,6 +510,7 @@ export class IntegrationService { ); // @ts-ignore - return integrationInstance[data.funcName](integration, plugData); + const ids = await integrationInstance[data.funcName](integration, plugData, this.loadExisingData.bind(this)); + return this._integrationRepository.saveExisingData(data.funcName, data.integrationId, ids); } } diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 214090d5..ca128f26 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -266,6 +266,7 @@ model Integration { postingTimes String @default("[{\"time\":120}, {\"time\":400}, {\"time\":700}]") customInstanceDetails String? plugs Plugs[] + exisingPlugData ExisingPlugData[] @@index([updatedAt]) @@index([deletedAt]) @@ -455,6 +456,16 @@ model Plugs { @@index([organizationId]) } +model ExisingPlugData { + id String @id @default(uuid()) + integrationId String + integration Integration @relation(fields: [integrationId], references: [id]) + methodName String + value String + + @@unique([integrationId, methodName, value]) +} + enum OrderStatus { PENDING ACCEPTED diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts index f6553c51..59ae0a71 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts @@ -111,7 +111,6 @@ export class LinkedinPageProvider ) ).json(); - console.log(all); return (elements || []).map((e: any) => ({ id: e.organizationalTarget.split(':').pop(), page: e.organizationalTarget.split(':').pop(), @@ -366,7 +365,7 @@ export class LinkedinPageProvider @Plug({ title: 'Auto Repost Posts', description: - 'When a post reached a certain number of likes, repost it to increase engagement', + 'When a post reached a certain number of likes, repost it to increase engagement (1 week old posts)', runEveryMilliseconds: 7200000, fields: [ { @@ -378,24 +377,109 @@ export class LinkedinPageProvider }, ], }) - async autoAddPost(integration: Integration, fields: { likesAmount: number }) { - const a = await fetch( - `https://api.linkedin.com/rest/posts?author=${encodeURIComponent( - `urn:li:organization:${integration.internalId}` - )}&q=author&count=10&sortBy=LAST_MODIFIED`, - { - method: 'GET', - headers: { - 'X-Restli-Protocol-Version': '2.0.0', - 'Content-Type': 'application/json', - 'LinkedIn-Version': '202402', - Authorization: `Bearer ${integration.token}`, - }, - } + async autoRepostPost( + integration: Integration, + fields: { likesAmount: number }, + loadExisingData: ( + methodName: string, + integrationId: string, + id: string[] + ) => Promise + ) { + const all = await // const { elements } = await ( + ( + await this.fetch( + `https://api.linkedin.com/rest/posts?author=${encodeURIComponent( + `urn:li:organization:${integration.internalId}` + )}&q=author&count=10&sortBy=LAST_MODIFIED`, + { + method: 'GET', + headers: { + 'X-Restli-Protocol-Version': '2.0.0', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + Authorization: `Bearer ${integration.token}`, + }, + } + ) + ).json(); + + // only post published in the last week + const lastWeekPosts = all.elements.filter((element: any) => { + const postDate = new Date(element.publishedAt).getTime(); + const weekAgo = new Date().getTime() - 604800000; + return postDate > weekAgo; + }); + + const getLastFiveLikes = await Promise.all( + lastWeekPosts.map(async (element: any) => { + const { + likesSummary: { totalLikes }, + } = await ( + await this.fetch( + `https://api.linkedin.com/v2/socialActions/${encodeURIComponent( + element.id + )}`, + { + method: 'GET', + headers: { + 'X-Restli-Protocol-Version': '2.0.0', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + Authorization: `Bearer ${integration.token}`, + }, + } + ) + ).json(); + + return { id: element.id, totalLikes }; + }) ); - console.log(await a.json()); - return; + const findLikes = getLastFiveLikes.filter( + (element) => element.totalLikes >= fields.likesAmount + ); + + if (findLikes.length === 0) { + return []; + } + + const checkIfAlreadyPosted = await loadExisingData( + 'autoRepostPost', + integration.id, + findLikes.map((p) => p.id) + ); + + if (checkIfAlreadyPosted.length === 0) { + return []; + } + + await this.fetch(`https://api.linkedin.com/rest/posts`, { + body: JSON.stringify({ + author: `urn:li:organization:${integration.internalId}`, + commentary: '', + visibility: 'PUBLIC', + distribution: { + feedDistribution: 'MAIN_FEED', + targetEntities: [], + thirdPartyDistributionChannels: [], + }, + lifecycleState: 'PUBLISHED', + isReshareDisabledByAuthor: false, + reshareContext: { + parent: checkIfAlreadyPosted[0], + }, + }), + method: 'POST', + headers: { + 'X-Restli-Protocol-Version': '2.0.0', + 'Content-Type': 'application/json', + 'LinkedIn-Version': '202402', + Authorization: `Bearer ${integration.token}`, + }, + }); + + return [checkIfAlreadyPosted[0]]; } }