diff --git a/CONTRIBUTORS.MD b/CONTRIBUTORS.MD new file mode 100644 index 0000000..e69de29 diff --git a/FigmaApp.ts b/FigmaApp.ts index 2d940c9..954dc09 100644 --- a/FigmaApp.ts +++ b/FigmaApp.ts @@ -63,10 +63,6 @@ export class FigmaApp extends App { persistence: IPersistence, modify: IModify ) { - console.log( - 'modal title - ', - context.getInteractionData().view.title.text - ); const user: IUser = context.getInteractionData().user; const room = await getRoom(read, user); if (room) { @@ -85,7 +81,7 @@ export class FigmaApp extends App { return await handler .run(context, room) .catch((err) => - console.log('error: submitting 2nd modal', err) + console.log('error: submitting Events modal', err) ); } else if ( context.getInteractionData().view.title.text === @@ -107,7 +103,6 @@ export class FigmaApp extends App { context.getInteractionData().view.title.text === modalTitle.REPLY_MODAL ) { - console.log('inside reply modal'); const handler = new ExecuteReplyHandler( this, read, @@ -120,7 +115,6 @@ export class FigmaApp extends App { context.getInteractionData().view.title.text === modalTitle.CREATE_COMMENT_MODAL ) { - console.log('inside create comment modal'); const handler = new CommentModalHandler( this, read, @@ -228,18 +222,26 @@ export class FigmaApp extends App { modify: IModify ): Promise { const user = context.user; - const welcomeMessage = `You’ve successfully installed Figma Rocket.Chat app! Now your Figma comments and notifications will show up here. :tada: + const welcomeMessage = `You’ve successfully installed Figma Rocket.Chat app! Now the admin of the server has to create an app on figma.com and add the figma client id and client secret to the app settings in order to connect the server with figma. + 1. Go to https://www.figma.com/developers/apps and create a new app. + 2. Get the callback url from the app settings page ( -> admin panel -> apps) in rocket.chat and add it to the figma app. + 3. Copy the client id and client secret and paste it in the app settings ( don't forget to click on save button ) + :tada: You are all set! + + Now your Figma comments and notifications will show up in the rocket chat server. With Figma App, you can reply to file comments directly in a rocket chat channel. You will get notified when: \xa0\xa0 • A new comment is added to a file you are collaborating on. \xa0\xa0 • Someone replies to a comment you made. \xa0\xa0 • You are tagged in a file. \xa0\xa0 • You are invited to a file. \xa0\xa0 • A file you are collaborating on is updated. + \xa0\xa0 • Notifications on Branches ( for organizations only ). Some tips: \xa0\xa0 • When you reply to a Figma comment here, your reply will automatically be added to the Figma file. \xa0\xa0 • Type \` /figma connect \` to connect your figma account to the rocket.chat server. - \xa0\xa0 • Type \` /figma help \` for command. `; + \xa0\xa0 • Type \` /figma help \` for all the commands command. + \xa0\xa0 • Subscribe a file, team, or project in a channel using \` /figma subscribe \` and notify all the users that they will have to authenticate their figma accounts using \` /figma connect \`. `; await sendDMToUser(read, modify, user, welcomeMessage, persistence); } diff --git a/README.md b/README.md index 0ef3022..70a94ac 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,25 @@ -## Getting Started -Now that you have generated a blank default Rocket.Chat App, what are you supposed to do next? -Start developing! Open up your favorite editor, our recommended one is Visual Studio code, -and start working on your App. Once you have something ready to test, you can either -package it up and manually deploy it to your test instance or you can use the CLI to do so. -Here are some commands to get started: -- `rc-apps package`: this command will generate a packaged app file (zip) which can be installed **if** it compiles with TypeScript -- `rc-apps deploy`: this will do what `package` does but will then ask you for your server url, username, and password to deploy it for you +# Figma Notification now in Rocket.Chat -## Documentation -Here are some links to examples and documentation: -- [Rocket.Chat Apps TypeScript Definitions Documentation](https://rocketchat.github.io/Rocket.Chat.Apps-engine/) -- [Rocket.Chat Apps TypeScript Definitions Repository](https://github.com/RocketChat/Rocket.Chat.Apps-engine) -- [Example Rocket.Chat Apps](https://github.com/graywolf336/RocketChatApps) -- Community Forums - - [App Requests](https://forums.rocket.chat/c/rocket-chat-apps/requests) - - [App Guides](https://forums.rocket.chat/c/rocket-chat-apps/guides) - - [Top View of Both Categories](https://forums.rocket.chat/c/rocket-chat-apps) -- [#rocketchat-apps on Open.Rocket.Chat](https://open.rocket.chat/channel/rocketchat-apps) +![Horizontal Banner](https://github.com/irffanasiff/Apps.Figma/blob/versions_delete_notifications/assets/FigmaAppCover.png) -## Figma App +Rocket.Chat and Figma integration offers figma notifications directly inside rocket.chat channels. Teams/individuals will receive personalized updates for different events from figma for a project, file or a team they have subscribed to. This will improve workflow and the functionality of design teams so that teams can deliver better products faster. -- User can subscribe to only those files which are available under a team -- Users will be notified for files that are available to everyone on the team, or are in view-only projects. -- Users will not be notified for files in invite-only projects. +

App Features

+ + +

Setting up Figma App

+ + 1. Install the app from the [marketplace](https://www.rocket.chat/marketplace) or clone the repository and add the zip file manually to your rocket.chat server.
+ 2. Go to [figma api](https://www.figma.com/developers/apps) and create a figma app.
+![Figma app registration](https://github.com/irffanasiff/Apps.Figma/blob/versions_delete_notifications/assets/NewFigmaAppSS.png) + 3. Get the callback url from the app settings page in rocket.chat and add it to the figma app.
+ 4. Copy the client id and client secret and paste it in the app settings ( don't forget to click on save button )
+ 5. You are all set to go now. use `/figma connect` command to connect your figma account with rocket.chat.
diff --git a/app.json b/app.json index bd3306c..256deaf 100644 --- a/app.json +++ b/app.json @@ -8,7 +8,7 @@ "homepage": "https://irfanasif.me", "support": "https://github.com/demonicirfan" }, - "name": "Figma", + "name": "Figma Notifications (Beta)", "nameSlug": "figma", "classFile": "FigmaApp.ts", "description": "Integrates Figma into Rocket.Chat. Users and Teams are directly notified in the chat about comments and updates in the Figma project.", diff --git a/assets/AppsDetailsSS.png b/assets/AppsDetailsSS.png new file mode 100644 index 0000000..46dac18 Binary files /dev/null and b/assets/AppsDetailsSS.png differ diff --git a/assets/AuthSS.png b/assets/AuthSS.png new file mode 100644 index 0000000..8e6f41b Binary files /dev/null and b/assets/AuthSS.png differ diff --git a/assets/FigmaAppCover.png b/assets/FigmaAppCover.png new file mode 100644 index 0000000..f84f4a7 Binary files /dev/null and b/assets/FigmaAppCover.png differ diff --git a/assets/NewFigmaAppSS.png b/assets/NewFigmaAppSS.png new file mode 100644 index 0000000..8a39c8e Binary files /dev/null and b/assets/NewFigmaAppSS.png differ diff --git a/assets/SettingsSS.png b/assets/SettingsSS.png new file mode 100644 index 0000000..9841115 Binary files /dev/null and b/assets/SettingsSS.png differ diff --git a/command/Connect.ts b/command/Connect.ts deleted file mode 100644 index cdd3860..0000000 --- a/command/Connect.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { FigmaApp } from "../FigmaApp"; -import { - IRead, - IModify, - IHttp, - IPersistence, -} from "@rocket.chat/apps-engine/definition/accessors"; -import { IUser } from "@rocket.chat/apps-engine/definition/users"; -import { createSectionBlock, IButton } from "../src/lib/block"; -import { sendDMToUser } from "../src/lib/messages"; - -export async function figmaConnectCommand( - app: FigmaApp, - read: IRead, - modify: IModify, - user: IUser, - persistence: IPersistence -) { - const url = await app - .getOauth2ClientInstance() - .getUserAuthorizationUrl(user); - - const button: IButton = { - text: "Connect Your Account", - url: url.toString(), - }; - - const message = ` - Connect your Figma account to start getting notifications. - With Figma App, you can reply to file comments directly in a rocket chat channel. You will get notified when: - \xa0\xa0 • A new comment is added to a file you are collaborating on. - \xa0\xa0 • Someone replies to a comment you made. - \xa0\xa0 • You are tagged in a file. - \xa0\xa0 • You are invited to a file. - \xa0\xa0 • A file you are collaborating on is updated.`; - - const block = await createSectionBlock(modify, message, button); - await sendDMToUser(read, modify, user, "", persistence, block); -} diff --git a/command/FigmaCommand.ts b/command/FigmaCommand.ts deleted file mode 100644 index c4266ae..0000000 --- a/command/FigmaCommand.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { - ISlashCommand, - SlashCommandContext, -} from "@rocket.chat/apps-engine/definition/slashcommands"; -import { FigmaApp } from "../FigmaApp"; -import { - IRead, - IModify, - IHttp, - IPersistence, -} from "@rocket.chat/apps-engine/definition/accessors"; -import { IUser } from "@rocket.chat/apps-engine/definition/users"; -import { createSectionBlock, IButton } from "../src/lib/block"; -import { - sendDMToUser, - sendMessage, - sendNotificationToUsers, -} from "../src/lib/messages"; -import { IRoom } from "@rocket.chat/apps-engine/definition/rooms"; -import { getAccessTokenForUser } from "../src/storage/users"; -import { - BlockElementType, - TextObjectType, -} from "@rocket.chat/apps-engine/definition/uikit"; -import { figmaSubscribeCommand } from "./Subscribe"; -import { figmaConnectCommand } from "./Connect"; - -export class FigmaCommand implements ISlashCommand { - public command = "figma"; - public i18nParamsExample = "params_example"; - public i18nDescription = "cmd_description"; - public providesPreview = false; - - public constructor(private readonly app: FigmaApp) {} - - public async executor( - context: SlashCommandContext, - read: IRead, - modify: IModify, - http: IHttp, - persistence: IPersistence - ): Promise { - const [command] = context.getArguments(); - switch (command) { - case "connect": - await figmaConnectCommand( - this.app, - read, - modify, - context.getSender(), - persistence - ); - break; - case "help": - await this.figmaHelpCommand( - read, - modify, - context.getSender(), - persistence - ); - break; - case "subscribe": - await figmaSubscribeCommand( - context, - read, - modify, - http, - persistence, - context.getRoom(), - context.getSender() - ); - break; - default: - await this.figmaConfuseCommand( - context.getRoom(), - read, - modify, - context.getSender(), - persistence - ); - break; - } - } - - public async figmaHelpCommand( - read: IRead, - modify: IModify, - user: IUser, - persistence: IPersistence - ) { - const message = `Commands available inside a channel: - \xa0\xa0• To connect your Figma account with the rocket chat server use command \`/figma connect\`. - \xa0\xa0• To subscribe for updates to a any file/project from figma inside rocket chat use command \`/figma subscribe\`. - \xa0\xa0 • To unsubscribe to a file inside a channel use \`/figma unsubscribe\`. - - Commands available inside Direct Messages: - \xa0\xa0• \` /figma off \` to turn off notifications. - \xa0\xa0• \` /figma on \` to turn notifications back on. - `; - await sendDMToUser(read, modify, user, message, persistence); - } - public async figmaConfuseCommand( - room: IRoom, - read: IRead, - modify: IModify, - user: IUser, - persistence: IPersistence - ) { - const message = `Hmmm. I didn't really understand that last message. - Try \`/figma help\` to see the commands available - `; - await sendNotificationToUsers(read, modify, user, room, message); - } -} diff --git a/command/Subscribe.ts b/command/Subscribe.ts deleted file mode 100644 index 5892e31..0000000 --- a/command/Subscribe.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { SlashCommandContext } from "@rocket.chat/apps-engine/definition/slashcommands"; -import { - IRead, - IModify, - IHttp, - IPersistence, -} from "@rocket.chat/apps-engine/definition/accessors"; -import { IUser } from "@rocket.chat/apps-engine/definition/users"; -import { sendDMToUser, sendNotificationToUsers } from "../src/lib/messages"; -import { IRoom } from "@rocket.chat/apps-engine/definition/rooms"; -import { getAccessTokenForUser } from "../src/storage/users"; -import { - BlockElementType, - TextObjectType, -} from "@rocket.chat/apps-engine/definition/uikit"; -import { - RocketChatAssociationModel, - RocketChatAssociationRecord, -} from "@rocket.chat/apps-engine/definition/metadata"; -import { uuid } from "../src/lib/uuid"; - -export async function figmaSubscribeCommand( - context: SlashCommandContext, - read: IRead, - modify: IModify, - http: IHttp, - persist: IPersistence, - room: IRoom, - sender: IUser, - id?: string -) { - const viewId = id || uuid(); - const accessToken = await getAccessTokenForUser(read, sender); - if (!accessToken?.token) { - const message = `Your have not connected your account yet. Use \`/figma connect\` to connect your account.`; - await sendNotificationToUsers(read, modify, sender, room, message); - return; - } - - if (room.type === "d") { - const message = `You can only subscribe to files inside a channel for notifications. Try \`/figma help\` `; - await sendDMToUser(read, modify, sender, message, persist); - return; - } - - const triggerId = context.getTriggerId()!; - - const association = new RocketChatAssociationRecord( - RocketChatAssociationModel.MISC, - sender.id - ); - await persist.createWithAssociation(room, association); - - const block = modify.getCreator().getBlockBuilder(); - - block.addSectionBlock({ - text: { - text: "Subscribe your Rocket Chat channel to notifications about files, teams, or projects. You can only subscribe to resources were you have edit access.", - type: TextObjectType.PLAINTEXT, - }, - }); - - block.addDividerBlock(); - block.addSectionBlock({ - text: { - text: "Select a resource to subscribe to", - type: TextObjectType.PLAINTEXT, - }, - }); - - block.addActionsBlock({ - blockId: "type", - elements: [ - block.newStaticSelectElement({ - placeholder: block.newPlainTextObject("File"), - actionId: "type", - initialValue: "file", - options: [ - { - text: block.newPlainTextObject("File"), - value: "file", - }, - { - text: block.newPlainTextObject("Team"), - value: "team", - }, - { - text: block.newPlainTextObject("Project"), - value: "project", - }, - ], - }), - ], - }); - block.addInputBlock({ - label: { - text: "Enter URL of the team or file", - type: TextObjectType.PLAINTEXT, - }, - blockId: "URL", - element: { - actionId: "URL", - placeholder: { - text: "Enter the URL", - type: TextObjectType.PLAINTEXT, - }, - type: BlockElementType.PLAIN_TEXT_INPUT, - }, - }); - - return await modify.getUiController().openModalView( - { - id: "modelView", - title: block.newPlainTextObject("Get Figma Notifications"), - close: block.newButtonElement({ - text: block.newPlainTextObject("Cancel"), - }), - submit: block.newButtonElement({ - text: block.newPlainTextObject("Submit"), - }), - blocks: block.getBlocks(), - }, - { triggerId }, - sender - ); -} diff --git a/i18n/en.json b/i18n/en.json index b99f803..6bbfda1 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1,5 +1,5 @@ { "cmd_description": "Get Figma notifications in your channel", - "params_example": "help | connect | subscribe | unsubscribe " + "params_example": "help | connect | subscribe | list " } diff --git a/icon.png b/icon.png index 9e07b2d..2548abd 100644 Binary files a/icon.png and b/icon.png differ diff --git a/icon2.png b/icon2.png deleted file mode 100644 index f766972..0000000 Binary files a/icon2.png and /dev/null differ diff --git a/src/command/FigmaCommand.ts b/src/command/FigmaCommand.ts index 3bcade1..0aa6c22 100644 --- a/src/command/FigmaCommand.ts +++ b/src/command/FigmaCommand.ts @@ -115,12 +115,8 @@ export class FigmaCommand implements ISlashCommand { ): Promise { const message = `Commands available inside a channel: \xa0\xa0• To connect your Figma account with the rocket chat server use command \`/figma connect\`. - \xa0\xa0• To subscribe for updates to a any file/project from figma inside rocket chat use command \`/figma subscribe\`. - \xa0\xa0 • To unsubscribe to a file inside a channel use \`/figma unsubscribe\`. - - Commands available inside Direct Messages: - \xa0\xa0• \` /figma off \` to turn off notifications. - \xa0\xa0• \` /figma on \` to turn notifications back on. + \xa0\xa0• To subscribe for updates to a any file/project/team from figma inside rocket chat use command \`/figma subscribe\`. + \xa0\xa0 • To list all the subscriptions use \`/figma list\`. `; await sendDMToUser(read, modify, user, message, persistence); } diff --git a/src/endpoints/events.ts b/src/endpoints/events.ts index f81f075..2fd4201 100644 --- a/src/endpoints/events.ts +++ b/src/endpoints/events.ts @@ -4,7 +4,20 @@ import { IRead, IPersistence } from '@rocket.chat/apps-engine/definition/accessors'; -import { ICommentPayload, ISubscription } from '../definition'; +import { + BlockElementType, + TextObjectType +} from '@rocket.chat/apps-engine/definition/uikit'; +import { + ICommentPayload, + IDeletePayload, + ISubscription, + IVersionUpdatePayload, + NewIUser +} from '../definition'; +import { blockAction, events } from '../enums/enums'; +import { getRequest } from '../helpers/Figma.sdk'; +import { botMessageChannel, botNormalMessageChannel } from '../lib/messages'; import { getAllUsers } from '../storage/users'; import { commentEvent as useCommentEvent } from './commentEvent'; @@ -68,13 +81,56 @@ export async function commentEvent( } } export async function deleteEvent( - payload: ICommentPayload, + payload: IDeletePayload, subscriptions: ISubscription[], modify: IModify, read: IRead, http: IHttp ) { - // + // send request to ffigma if that file exists in figma only then proceed further + if (payload.event_type !== events.DELETE) return; + + // find user by payload.triggered_by.id + // get user id from db which was stored while authenticating + let deletedBy: NewIUser | undefined; + await getAllUsers(read).then((user) => { + deletedBy = user.find( + (user) => user.figmaUserId === payload.triggered_by.id + ); + }); + + for (const subscription of subscriptions) { + for (const roomData of subscription.room_data) { + const room = await read.getRoomReader().getById(roomData.room_Id!); + if (!room) { + console.log('error: no room found '); + return; + } + // check if the current room contains the file inside file_ids array + if (roomData.file_Ids?.includes(payload.file_key)) { + if (deletedBy) { + // send message with deleteBy to all the users in the room + await botNormalMessageChannel( + read, + modify, + room, + `File: ${payload.file_name} was deleted by @${deletedBy.username}` + ); + } else { + await botNormalMessageChannel( + read, + modify, + room, + `File: ${payload.file_name} was deleted by @${payload.triggered_by.handle}` + ); + } + } else { + console.log( + 'RESULT: the file which was deleted is not subscribed by the user' + ); + } + } + } } export async function updateEvent( payload: ICommentPayload, @@ -83,7 +139,7 @@ export async function updateEvent( read: IRead, http: IHttp ) { - // + // console.log('payload for file saved - ', payload); } export async function publishEvent( payload: ICommentPayload, @@ -95,11 +151,74 @@ export async function publishEvent( // } export async function versionUpdateEvent( - payload: ICommentPayload, + payload: IVersionUpdatePayload, subscriptions: ISubscription[], modify: IModify, read: IRead, http: IHttp ) { - // + // send request to ffigma if that file exists in figma only then proceed further + if (payload.event_type !== events.VERSION_UPDATE) return; + // find user by payload.triggered_by.id + // get user id from db which was stored while authenticating + let versionAddedBy: NewIUser | undefined; + await getAllUsers(read).then((user) => { + versionAddedBy = user.find( + (user) => user.figmaUserId === payload.triggered_by.id + ); + }); + for (const subscription of subscriptions) { + for (const roomData of subscription.room_data) { + const room = await read.getRoomReader().getById(roomData.room_Id!); + if (!room) { + return; + } + // check if the current room contains the file inside file_ids array + let message: string | undefined = undefined; + if (roomData.file_Ids?.includes(payload.file_key)) { + if (payload.description.length > 0) { + message = `Description: ${payload.description}`; + } + if (versionAddedBy) { + // send message with deleteBy to all the users in the room + const sendMessage = await botNormalMessageChannel( + read, + modify, + room, + `@${versionAddedBy?.username} added a new version: *${ + payload?.label + }* of the file: [${ + payload?.file_name + }](https://www.figma.com/file/${payload?.file_key}/${ + payload?.file_name + }?version-id=${payload?.version_id}) \n ${ + message && message + }` + ); + console.log('was message sent ', sendMessage); + } else { + await botNormalMessageChannel( + read, + modify, + room, + `${ + payload?.triggered_by?.handle + } added a new version: *${ + payload?.label + }* of the file: [${ + payload?.file_name + }](https://www.figma.com/file/${payload?.file_key}/${ + payload?.file_name + }?version-id=${payload?.version_id}) \n ${ + message && message + }` + ); + } + } else { + console.log( + 'RESULT: this file is not subscribed by the user so no need to send message' + ); + } + } + } } diff --git a/src/endpoints/figmaEndpoints.ts b/src/endpoints/figmaEndpoints.ts index 89a77b2..1181b97 100644 --- a/src/endpoints/figmaEndpoints.ts +++ b/src/endpoints/figmaEndpoints.ts @@ -11,9 +11,19 @@ import { IApiResponse } from '@rocket.chat/apps-engine/definition/api'; import { Subscription } from '../sdk/webhooks.sdk'; -import { ICommentPayload, ISubscription } from '../definition'; +import { + ICommentPayload, + IDeletePayload, + ISubscription, + IVersionUpdatePayload +} from '../definition'; import { events } from '../enums/enums'; -import { commentEvent } from './events'; +import { + commentEvent, + deleteEvent, + updateEvent, + versionUpdateEvent +} from './events'; export class figmaWebHooks extends ApiEndpoint { public path = 'figmawebhook'; @@ -40,7 +50,6 @@ export class figmaWebHooks extends ApiEndpoint { if (payload.event_type === events.PING) { // todo : send message to the user that a new connection was made successfully - //await sendMessage(modify, room, user, 'New connection made successfully'); return this.success(); } @@ -49,20 +58,14 @@ export class figmaWebHooks extends ApiEndpoint { read.getPersistenceReader() ); - // Search subscriptions by webhook id in the stored subscriptions const subscriptions: ISubscription[] = await subscription.getSubscriptionsByHookID(payload.webhook_id); // todo : handle if there are multiple webhooks for a single file if (!subscriptions || subscriptions.length == 0) { - console.log( - '❌ Figma Pinged but No subscriptions found - ', - payload.webhook_id - ); return this.success(); } const eventCaps = payload.event_type.toUpperCase(); - - // switch case statement for event types + console.log('event - ', eventCaps); switch (eventCaps) { case events.COMMENT: await commentEvent( @@ -75,20 +78,30 @@ export class figmaWebHooks extends ApiEndpoint { ); break; - // case events.DELETE: - // // send message to the rooms for the delete event - // break; - // case events.UPDATE: - // // send message to the rooms for the update event - // break; - // case events.LIBRARY_PUBLISHED: - // // send message to the rooms for the create event - // break; - // case events.VERSION_UPDATE: - // // send message to the rooms for the version update event - // break; + case events.DELETE: + await deleteEvent( + payload as IDeletePayload, + subscriptions, + modify, + read, + http + ); + break; + case events.UPDATE: + await updateEvent(payload, subscriptions, modify, read, http); + break; + case events.LIBRARY_PUBLISHED: + break; + case events.VERSION_UPDATE: + await versionUpdateEvent( + payload as IVersionUpdatePayload, + subscriptions, + modify, + read, + http + ); + break; default: - // send message to the rooms for error return this.success(); } return this.success(); diff --git a/src/handlers/BlockActionHandler.ts b/src/handlers/BlockActionHandler.ts index c0d20a6..3b81b76 100644 --- a/src/handlers/BlockActionHandler.ts +++ b/src/handlers/BlockActionHandler.ts @@ -74,7 +74,6 @@ export class BlockActionHandler { ); break; case blockAction.REPLY: - console.log('inside reply to comment'); await commentReply( modify, context, @@ -87,10 +86,8 @@ export class BlockActionHandler { ); break; case blockAction.REACT: - console.log('result: reaction'); break; case blockAction.POST: - console.log('inside post reply'); break; case blockAction.COMMENT: await newComment( diff --git a/src/handlers/action.ts b/src/handlers/action.ts index 0126dc4..6df1589 100644 --- a/src/handlers/action.ts +++ b/src/handlers/action.ts @@ -50,14 +50,15 @@ export class BlockActionHandler { Authorization: `Bearer ${token?.token}` }; - const teamId: string = getTeamID(team_url); + const teamId: string | undefined = getTeamID(team_url); - if (teamId.length < 0) { - sendMessage( + if (!teamId || teamId.length < 0) { + botNotifyCurrentUser( + this.read, this.modify, - this.room, this.user, - 'Team Id Not Available' + this.room, + 'Cannot get the team ID from url. Please check if the URL is a Team URL.' ); return { success: false @@ -79,7 +80,7 @@ export class BlockActionHandler { this.modify, this.user, this.room, - 'Error connecting with figma server. Please try again after some time. Check logs if it still does not work report the issue' + 'Error connecting with figma server. Please try again after some time. Check logs if it still does not work report the issue.' ); //return { success: false }; } diff --git a/src/handlers/comment.ts b/src/handlers/comment.ts index 2454a41..e875a26 100644 --- a/src/handlers/comment.ts +++ b/src/handlers/comment.ts @@ -34,16 +34,12 @@ export class CommentModalHandler { message: view.state.new_comment.comment } }; - console.log('post data - ', postData, view.commentData); this.http .post( `https://api.figma.com/v1/files/${view.commentData}/comments`, postData ) .then(async (res) => { - - console.log('post comment data ', res); // remove this - // if res.data.status starts with 400 then show error message if (res.data.status === 404) { return await botNotifyCurrentUser( this.read, @@ -80,7 +76,7 @@ export class CommentModalHandler { ); }); } else { - console.log('room not found'); + console.log('error: room not found'); } return { success: true diff --git a/src/handlers/reply.ts b/src/handlers/reply.ts index 4a366cf..19dfac1 100644 --- a/src/handlers/reply.ts +++ b/src/handlers/reply.ts @@ -35,8 +35,6 @@ export class ExecuteReplyHandler { comment_id: view.commentData.commentId } }; - console.log('post data - ', postData, view.block); - this.http .post( `https://api.figma.com/v1/files/${view.commentData.fileKey}/comments`, diff --git a/src/handlers/submit.ts b/src/handlers/submit.ts index 201a7bb..11094b2 100644 --- a/src/handlers/submit.ts +++ b/src/handlers/submit.ts @@ -36,11 +36,11 @@ export class ExecuteViewSubmitHandler { }; } else { state = view.state as IState; - + console.log('state', state); const team_url = state?.team_url?.url; const resource_type = state?.resource_block?.type; const { user } = context.getInteractionData(); - + if (room) { if (!resource_type) { botNotifyCurrentUser( diff --git a/src/handlers/subscription/updateSubscriptionHandler.ts b/src/handlers/subscription/updateSubscriptionHandler.ts index b80a413..1c1788b 100644 --- a/src/handlers/subscription/updateSubscriptionHandler.ts +++ b/src/handlers/subscription/updateSubscriptionHandler.ts @@ -160,7 +160,7 @@ export async function updateSubscriptionHandler( user.id ) .then((res) => { - console.log('8 - updated subscription'); + console.log(' do: updated subscription'); return { success: true }; }) .catch((err) => { diff --git a/src/lib/commentReply.ts b/src/lib/commentReply.ts index be444f2..e9a7b5b 100644 --- a/src/lib/commentReply.ts +++ b/src/lib/commentReply.ts @@ -44,7 +44,6 @@ export async function commentReply( if (block.type === 'actions') { commentData = block?.elements[1].value; } - //console.log('commentId', commentId); } else { console.log('error: block is undefined - ', block); } diff --git a/src/lib/createSubscriptionMessage.ts b/src/lib/createSubscriptionMessage.ts deleted file mode 100644 index 7bbd125..0000000 --- a/src/lib/createSubscriptionMessage.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { - IHttp, - IHttpRequest, - IModify, - IPersistence, - IRead, -} from "@rocket.chat/apps-engine/definition/accessors"; -import { - BlockElementType, - IBlock, - TextObjectType, - UIKitViewSubmitInteractionContext, -} from "@rocket.chat/apps-engine/definition/uikit"; -import { IUIKitViewSubmitIncomingInteraction } from "@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionTypes"; -import { FigmaApp } from "../../FigmaApp"; -import { getAccessTokenForUser } from "../storage/users"; -import { FigmaSDK, getFileID, getProjectID, getTeamID } from "./sdk"; -import { - appUserSendMessage, - sendDMToUser, - sendMessage, - sendNotificationToUsers, -} from "./messages"; -import { IState } from "../definition"; -import { IUser } from "@rocket.chat/apps-engine/definition/users"; -import { IRoom } from "@rocket.chat/apps-engine/definition/rooms"; -import { - RocketChatAssociationModel, - RocketChatAssociationRecord, -} from "@rocket.chat/apps-engine/definition/metadata"; -import { IModalContext } from "../definition"; - -export async function createSubscription( - context: UIKitViewSubmitInteractionContext, - data: IUIKitViewSubmitIncomingInteraction, - read: IRead, - http: IHttp, - modify: IModify, - persistence: IPersistence -) { - const { state }: IState = data.view as any; - const user: IUser = context.getInteractionData().user; - const association = new RocketChatAssociationRecord( - RocketChatAssociationModel.MISC, - user.id - ); - const [record] = (await read - .getPersistenceReader() - .readByAssociation(association)) as Array; - - const room = await read.getRoomReader().getById(record.id!); - const token = await getAccessTokenForUser(read, user); - - const headers: any = { - Authorization: `Bearer ${token?.token}`, - }; - console.log("type - ", state.type.type); - if (state.type.type === "file") { - const fileId = getFileID(state.URL.URL); - - // if user inserts a wrong url - if (fileId.length !== 22) { - if (room) { - sendNotificationToUsers( - read, - modify, - user, - room, - "The File URL Entered by you is wrong" - ); - } - return; - } - - const response = await http.get( - `https://api.figma.com/v1/files/${fileId}`, - { headers } - ); - - if (room) { - // image block builder - const block = modify.getCreator().getBlockBuilder(); - block.addSectionBlock({ - text: { - text: `${user.name} subscribed to ${response.data.name}`, - type: TextObjectType.PLAINTEXT, - }, - }); - block.addImageBlock({ - imageUrl: response.data.thumbnailUrl, - altText: response.data.name, - }); - //create a button block to open the file in figma - block.addActionsBlock({ - elements: [ - block.newButtonElement({ - text: block.newPlainTextObject("Open in Figma"), - actionId: "open", - url: state.URL.URL, - }), - ], - }); - - appUserSendMessage(read, modify, room, block); - } - return; - } else if (state.type.type === "team") { - const teamId = getTeamID(state.URL.URL); - - if (teamId.length !== 19) { - if (room) { - sendNotificationToUsers( - read, - modify, - user, - room, - "The Team URL Entered by you is wrong" - ); - } - return; - } - const response = await http.get( - `https://api.figma.com/v1/teams/${teamId}/projects`, - { headers } - ); - - if (room) { - const block = modify.getCreator().getBlockBuilder(); - block.addSectionBlock({ - text: { - text: `${user.name} subscribed to ${response.data.name}`, - type: TextObjectType.PLAINTEXT, - }, - }); - response.data.projects.forEach((project) => { - block.addActionsBlock({ - elements: [ - block.newButtonElement({ - text: block.newPlainTextObject(project.name), - actionId: "subscribe", - url: `https://www.figma.com/files/project/${project.id}`, - }), - ], - }); - }); - appUserSendMessage(read, modify, room, block); - return; - } - } else if (state.type.type === "project") { - const projectID = getProjectID(state.URL.URL); - - if (projectID.length !== 8) { - if (room) { - sendNotificationToUsers( - read, - modify, - user, - room, - "The Project URL Entered by you is wrong" - ); - } - return; - } - - const response = await http.get( - `https://api.figma.com/v1/projects/${projectID}/files`, - { headers } - ); - - // if room exists then create a button block with all the files from the response and send it to the user - if (room) { - const block = modify.getCreator().getBlockBuilder(); - block.addSectionBlock({ - text: { - text: `${user.name} subscribed to ${response.data.name}`, - type: TextObjectType.PLAINTEXT, - }, - }); - response.data.files.forEach((file) => { - block.addActionsBlock({ - elements: [ - block.newButtonElement({ - text: block.newPlainTextObject(file.name), - actionId: "subscribe", - url: `https://www.figma.com/files/${file.id}`, - }), - ], - }); - }); - appUserSendMessage(read, modify, room, block); - return; - } - } -} diff --git a/src/lib/getFiles.ts b/src/lib/getFiles.ts index a4f5a2e..1a003d3 100644 --- a/src/lib/getFiles.ts +++ b/src/lib/getFiles.ts @@ -62,7 +62,6 @@ export async function getFiles( for (const subscription of subscriptions) { const roomData: storedRoomData[] = subscription.room_data; for (const room_data of roomData) { - console.log('room data', room_data); if (room_data.room_Id === room.id && room_data.file_Ids) { room_files_ids.push(...room_data.file_Ids); } @@ -102,7 +101,6 @@ export async function getFiles( fileDetails.push({ name, apiUrl, id }); return; }); - console.log('files data - ', fileDetails); const block = modify.getCreator().getBlockBuilder(); block.addSectionBlock({ text: { @@ -158,7 +156,6 @@ export async function getFiles( } }) .catch(async (e) => { - console.log('error is - ', e); return await botNotifyCurrentUser( read, modify, diff --git a/src/lib/getProjects.ts b/src/lib/getProjects.ts index 76aa17a..65aedd8 100644 --- a/src/lib/getProjects.ts +++ b/src/lib/getProjects.ts @@ -73,7 +73,6 @@ export async function getProjects( } } if (room_projects_ids.length === 0) { - console.log('send error message'); return await botNotifyCurrentUser( read, modify, @@ -161,7 +160,6 @@ export async function getProjects( } }) .catch(async (error) => { - console.log('error: getting all subscriptions - ', error); return await botNotifyCurrentUser( read, modify, diff --git a/src/lib/messages.ts b/src/lib/messages.ts index 1dce59a..64b9ddf 100644 --- a/src/lib/messages.ts +++ b/src/lib/messages.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-mixed-spaces-and-tabs */ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable prefer-const */ import { IModify, @@ -93,33 +91,44 @@ export async function botMessageChannel( if (blocks !== undefined) { msg.setBlocks(blocks); } - return modify.getCreator().finish(msg); + modify.getCreator().finish(msg); + return 'message sent successfully'; } - console.log('app user not found user reader - ', read.getUserReader()); + console.log( + 'error: app user not found user reader - ', + read.getUserReader() + ); return ''; } -export async function appUserSendMessage( +export async function botNormalMessageChannel( read: IRead, modify: IModify, room: IRoom, + message: string, blocks?: BlockBuilder | [IBlock] ): Promise { - const appUser = (await read.getUserReader().getAppUser()) as IUser; - const msg = modify - .getCreator() - .startMessage() - .setSender(appUser) - .setRoom(room) - .setGroupable(false) - .setParseUrls(false); - - if (blocks !== undefined) { - msg.setBlocks(blocks); + const appUser = await read.getUserReader().getAppUser(); + if (appUser) { + const msg = modify + .getCreator() + .startMessage() + .setSender(appUser) + .setRoom(room) + .setGroupable(false) + .setParseUrls(false) + .setText(message); + console.log('msg', msg); + if (blocks !== undefined) { + msg.setBlocks(blocks); + } + return modify.getCreator().finish(msg); } - - return await modify.getCreator().finish(msg); + console.log( + 'error: app user not found user reader - ', + read.getUserReader() + ); + return ''; } - export async function shouldSendMessage( read: IRead, persistence: IPersistence, @@ -140,7 +149,6 @@ export async function shouldSendMessage( * Figma.bot sends notification inside the current room to the current user */ export async function botNotifyCurrentUser( - read: IRead, modify: IModify, user: IUser, @@ -173,7 +181,7 @@ export async function sendDMToUser( user: IUser, message: string, persistence: IPersistence, - blocks?: BlockBuilder | [IBlock] + blocks?: BlockBuilder ): Promise { const appUser: IUser | undefined = await read.getUserReader().getAppUser(); if (appUser) { diff --git a/src/lib/sdk.ts b/src/lib/sdk.ts index 568d287..81fe5ba 100644 --- a/src/lib/sdk.ts +++ b/src/lib/sdk.ts @@ -1,85 +1,42 @@ -import { IHttp } from "@rocket.chat/apps-engine/definition/accessors"; -import { IUser } from "@rocket.chat/apps-engine/definition/users"; -const crypto = require("crypto"); +const BaseFileHost = 'https://www.figma.com/file/'; +const BaseTeamHost = 'https://www.figma.com/files/team/'; +const BaseProjectHost = 'https://www.figma.com/files/project/'; -const BaseFileHost = "https://www.figma.com/file/"; -const BaseTeamHost = "https://www.figma.com/files/team/"; -const BaseProjectHost = "https://www.figma.com/files/project/"; -const BaseApiHost = "https://www.figma.com/api"; -const passcode = crypto.randomBytes(48).toString("hex"); - -// figmaSDK class contains all the methods to interact with figma API -export class FigmaSDK { - constructor(private readonly http: IHttp, private readonly accessToken) {} - - public createWebhook(fileID: string, webhookUrl: string) { - return this.post("https://api.figma.com/v2/webhooks", { - active: true, - event_type: "FILE_COMMENT", - team_id: "1051788064684166795", - events: ["push"], - endpoint: webhookUrl, - passcode, - content_type: "json", - }); - } - - private async post(url: string, data: any): Promise { - const response = await this.http.post(url, { - headers: { - "X-Figma-Token": this.accessToken, - "Content-Type": "application/json", - "User-Agent": "Rocket.Chat-Apps-Engine", - }, - data, - }); - - console.log(response.statusCode); - // If it isn't a 2xx code, something wrong happened - if (!response.statusCode.toString().startsWith("2")) { - throw response; - } - console.log("figma response - ", response); - - return JSON.parse(response.content || "{}"); - } -} -//file url example - https://www.figma.com/file/b0l0lp73g04EgbqDeNiHh4/file-2 export function getFileName(fileURL: string): string { if (!fileURL.startsWith(BaseFileHost)) { - return ""; + return ''; } const apiUrl = fileURL.substring(BaseFileHost.length); - const fileName = apiUrl.split("/")[1].split("?")[0]; + const fileName = apiUrl.split('/')[1].split('?')[0]; return fileName; } // file url - https://www.figma.com/file/f6dNpg1iG3KXgy4BpPaKMK/Twitter-Content?node-id=0%3A1 export function getFileID(fileURL: string): string { if (!fileURL.startsWith(BaseFileHost)) { - return ""; + return ''; } const apiUrl = fileURL.substring(BaseFileHost.length); - const fileID = apiUrl.split("/")[0]; + const fileID = apiUrl.split('/')[0]; return fileID; } export function getTeamID(teamURL: string): string { if (!teamURL.startsWith(BaseTeamHost)) { - return ""; + return ''; } - const id = teamURL.substring(BaseTeamHost.length).split("/")[0]; + const id = teamURL.substring(BaseTeamHost.length).split('/')[0]; const teamID = id; return teamID; } // project url - https://www.figma.com/files/project/53807385/project1?fuid=983416835186317142 export function getProjectID(teamURL: string): string { if (!teamURL.startsWith(BaseProjectHost)) { - return ""; + return ''; } - const id = teamURL.substring(BaseProjectHost.length).split("/")[0]; + const id = teamURL.substring(BaseProjectHost.length).split('/')[0]; const projectID = id; return projectID; } diff --git a/src/modals/events.ts b/src/modals/events.ts index 72e5094..1aa365c 100644 --- a/src/modals/events.ts +++ b/src/modals/events.ts @@ -83,14 +83,14 @@ export async function eventModal({ emoji: true } }, - { - value: 'FILE_UPDATE', - text: { - type: TextObjectType.PLAINTEXT, - text: 'File Updates', - emoji: true - } - }, + // { + // value: 'FILE_UPDATE', + // text: { + // type: TextObjectType.PLAINTEXT, + // text: 'File Updates', + // emoji: true + // } + // }, { value: 'FILE_VERSION_UPDATE', text: { @@ -106,15 +106,15 @@ export async function eventModal({ text: 'File Delete', emoji: true } - }, - { - value: 'LIBRARY_PUBLISH', - text: { - type: TextObjectType.PLAINTEXT, - text: 'Library Publish', - emoji: true - } } + // { + // value: 'LIBRARY_PUBLISH', + // text: { + // type: TextObjectType.PLAINTEXT, + // text: 'Library Publish', + // emoji: true + // } + // } ], placeholder: { type: TextObjectType.PLAINTEXT, diff --git a/src/modals/subscription.ts b/src/modals/subscription.ts index 546864e..26477d1 100644 --- a/src/modals/subscription.ts +++ b/src/modals/subscription.ts @@ -63,7 +63,7 @@ export async function subscriptionsModal({ block.newStaticSelectElement({ placeholder: block.newPlainTextObject('File'), actionId: 'type', - initialValue: 'team', + initialValue: 'file', options: [ { text: block.newPlainTextObject('File'), diff --git a/src/sdk/subscription.sdk.ts b/src/sdk/subscription.sdk.ts index f29d206..f95b8ab 100644 --- a/src/sdk/subscription.sdk.ts +++ b/src/sdk/subscription.sdk.ts @@ -24,14 +24,17 @@ export function getFileID(fileURL: string): string { return fileID; } -export function getTeamID(teamURL: string): string { +export function getTeamID(teamURL: string): string | undefined { if (!teamURL.startsWith(BaseTeamHost)) { - return ''; + return undefined; } - const id = teamURL.substring(BaseTeamHost.length).split('/')[0]; const teamID = id; - return teamID; + if (teamID.length < 16) { + return undefined; + } else { + return teamID; + } } // Project url - https://www.figma.com/files/project/53807385/project1?fuid=983416835186317142 diff --git a/src/sdk/webhooks.sdk.ts b/src/sdk/webhooks.sdk.ts index 3d3224b..7f3384f 100644 --- a/src/sdk/webhooks.sdk.ts +++ b/src/sdk/webhooks.sdk.ts @@ -288,7 +288,6 @@ export class Subscription { public async deleteAllTeamSubscriptions( team_id: string ): Promise { - console.log('team id - ', team_id); try { const associations: RocketChatAssociationRecord[] = [ new RocketChatAssociationRecord( diff --git a/src/subscription/addSubscription.ts b/src/subscription/addSubscription.ts index d22c8dd..e3f902e 100644 --- a/src/subscription/addSubscription.ts +++ b/src/subscription/addSubscription.ts @@ -48,460 +48,416 @@ export class AddSubscription { const project_Ids: string[] | undefined = state?.selectedProjects?.projects; const file_Ids: string[] | undefined = state?.selectedFiles?.files; - const team_id = getTeamID(state?.team_url.url); + const team_id: string | undefined = getTeamID(state?.team_url.url); + console.log('team id - ', team_id); + + if ( + user.id === undefined || + team_id === undefined || + event_type === undefined + ) { + await botNotifyCurrentUser( + this.read, + this.modify, + user, + room, + 'Invalid Input !' + ); + return { success: false }; + } try { - if (user.id) { - if ( - typeof team_id === undefined || - typeof event_type === undefined - ) { - await botNotifyCurrentUser( - this.read, - this.modify, - user, - room, - 'Invalid Input !' - ); - } else { - const accessToken = await getAccessTokenForUser( - this.read, - user - ); - if (!accessToken) { - await botNotifyCurrentUser( + const accessToken = await getAccessTokenForUser(this.read, user); + if (!accessToken) { + await botNotifyCurrentUser( + this.read, + this.modify, + user, + room, + 'You are not connect to figma!' + ); + return { success: false }; + } + const url = await getWebhookUrl(this.app); + const subscriptionStorage = new Subscription( + this.persistence, + this.read.getPersistenceReader() + ); + + const createWebhookSubscription = new WebhookSubscription( + this.read, + this.http, + this.modify, + this.persistence + ); + + let count = 0; // this counter we will use if if there are only 4 or less hooks left to create in figma we will notify the use that subscription was unsuccessful and delete some in figma + subscriptionStorage // todo: fix this .then chain into async await + .getSubscriptionsByTeam(team_id) + // this is for team we fetch all the details of that team to store them + .then(async (subscriptions) => { + // eslint-disable-next-line prefer-const + let teamData: { + team_id: string; + projects: string[]; + files: string[]; + } = { + team_id: team_id, + projects: [], + files: [] + }; + const files_in_team: string[] = []; + const projects_in_team: string[] = []; + if (!project_Ids?.length && !file_Ids?.length) { + await getRequest( + this.read, + context, + this.http, + `https://api.figma.com/v1/teams/${team_id}/projects` + ) + .then(async (team_response) => { + const reqUrls = team_response.data.projects.map( + (project: any) => { + projects_in_team.push(project.id); + return `https://api.figma.com/v1/projects/${project.id}/files`; + } + ); + if (projects_in_team) { + try { + await Promise.all( + reqUrls.map( + async (url) => + await getRequest( + this.read, + context, + this.http, + url + ) + ) + ) + .then((project_data) => { + // todo: do it this way add two input block one for project and one for file and when someone selects a project display the files below it based on the selected project. + project_data.forEach( + (project) => { + project.data.files.forEach( + ( + file // fix this bug which occurs after assigning file type + ) => + files_in_team.push( + file.key + ) + ); + } + ); + }) + .catch(async () => { + // + }); + teamData.projects = + projects_in_team.slice(); + teamData.files = files_in_team.slice(); + } catch (e) { + // + } + } + }) + .catch((error) => { + console.log('error: ', error); + }); + } + if (subscriptions && subscriptions.length) { + for (const subscription of subscriptions) { + // for every subscription + // 1 - Inside this subscription 🤯 ' + // now we are entering the room data zone to modify it. + // now we are entering the room data zone to modify it.' + for (const room_data of subscription.room_data) { + // 2 - we are at room data level and this will run as many times as the number of room data we have 🏡 + const returnValue: { + success: boolean; + } = await updateSubscriptionHandler( + context, + this.persistence, + this.read, + this.http, + team_id, + room, + user, + room_data, + event_type, + subscription, + project_Ids, + file_Ids, + subscriptionStorage, + teamData + ); + + if (!returnValue.success) { + return; + } + } + } + + const block = this.modify + .getCreator() + .getBlockBuilder(); + + if (!project_Ids?.length && !file_Ids?.length) { + //todo: update this message later and add subscribed events too + block.addSectionBlock({ + text: { + text: `A new team subscriptions is created by *${user.name}*. You will start receiving notifications for updates and comments inside the team in this channel.> If you have not already connected your figma account, please do so by typing \`/figma\` in the channel.`, + type: TextObjectType.MARKDOWN + } + }); + // create button action block + block.addActionsBlock({ + elements: [ + block.newButtonElement({ + actionId: 'view_team', + text: block.newPlainTextObject( + 'View Team Inside Figma' + ), + url: `https://www.figma.com/files/team/${team_id}` + }) + ] + }); + } else if (project_Ids?.length && !file_Ids?.length) { + // get project details from figma + // todo: send all the projects details inside channel + block.addSectionBlock({ + text: { + text: `New subscriptions for projects is created by *${user.name}*. You will start receiving notifications for updates and comments inside the project in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, + type: TextObjectType.MARKDOWN + } + }); + + block.addActionsBlock({ + elements: [ + block.newButtonElement({ + actionId: 'view_projects', + text: block.newPlainTextObject( + 'View Projects Inside Figma' + ), + url: `https://www.figma.com/files/team/${team_id}` + }) + ] + }); + } else if (!project_Ids?.length && file_Ids?.length) { + block.addSectionBlock({ + text: { + text: `New subscriptions for Files is created by *${user.name}*. You will start receiving notifications for updates and comments from these files in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, + type: TextObjectType.MARKDOWN + } + }); + block.addActionsBlock({ + elements: [ + block.newButtonElement({ + actionId: 'view_files', + text: block.newPlainTextObject( + 'View files Inside Figma' + ), + url: `https://www.figma.com/files/team/${team_id}` + }) + ] + }); + } + await botMessageChannel( this.read, this.modify, - user, room, - 'You are not connect to figma!' + block ); } else { - const url = await getWebhookUrl(this.app); - const subscriptionStorage = new Subscription( - this.persistence, - this.read.getPersistenceReader() - ); - - const createWebhookSubscription = - new WebhookSubscription( + // 1 - no subscription found' + let counter = 0; + [ + events.COMMENT, + events.DELETE, + events.LIBRARY_PUBLISHED, + events.UPDATE, + events.VERSION_UPDATE + ].map(async (event) => { + // our main logic is to hook into figma 5 times for every event type + const data = { + event_type: event, + team_id: team_id, + endpoint: url, + passcode: room.id, // Send room id as passcode + description: room.id + }; + // we send request to figma webhook to create a hook for every event ( runs 5 times ) + await postRequest( this.read, + context, this.http, - this.modify, - this.persistence - ); - - let count = 0; // this counter we will use if if there are only 4 or less hooks left to create in figma we will notify the use that subscription was unsuccessful and delete some in figma - subscriptionStorage - .getSubscriptionsByTeam(team_id) - // this is for team we fetch all the details of that team to store them - .then(async (subscriptions) => { - // eslint-disable-next-line prefer-const - let teamData: { - team_id: string; - projects: string[]; - files: string[]; - } = { - team_id: team_id, - projects: [], - files: [] - }; - const files_in_team: string[] = []; - const projects_in_team: string[] = []; - if (!project_Ids?.length && !file_Ids?.length) { - await getRequest( - this.read, - context, - this.http, - `https://api.figma.com/v1/teams/${team_id}/projects` - ) - .then(async (team_response) => { - //console.log('response from figma for projects - ', team_response); - const reqUrls = - team_response.data.projects.map( - (project: any) => { - console.log(project.id); - projects_in_team.push( - project.id - ); - return `https://api.figma.com/v1/projects/${project.id}/files`; - } - ); - if (projects_in_team) { - try { - await Promise.all( - reqUrls.map( - async (url) => - await getRequest( - this.read, - context, - this.http, - url - ) - ) - ) - .then( - (project_data) => { - // todo: do it this way add two input block one for project and one for file and when someone selects a project display the files below it based on the selected project. - project_data.forEach( - ( - project - ) => { - project.data.files.forEach( - ( - file // fix this bug which occurs after assigning file type - ) => - files_in_team.push( - file.key - ) - ); - } - ); - } - ) - .catch(async () => { - // - }); - teamData.projects = - projects_in_team.slice(); - teamData.files = - files_in_team.slice(); - } catch (e) { - // - } - } - }) - .catch((error) => { - console.log('error: ', error); - }); - } - if (subscriptions && subscriptions.length) { - for (const subscription of subscriptions) { - // for every subscription - // 1 - Inside this subscription 🤯 ' - // now we are entering the room data zone to modify it. - // now we are entering the room data zone to modify it.' - for (const room_data of subscription.room_data) { - // 2 - we are at room data level and this will run as many times as the number of room data we have 🏡 - const returnValue: { - success: boolean; - } = await updateSubscriptionHandler( - context, - this.persistence, + 'https://api.figma.com/v2/webhooks', + data + ) + .then(async (response) => { + // when a hook is created successfully we save it in db + if (response.data.error === true) { + // todo : manage this logic in a better way which covers all the edge cases. If for any request there is a error response then delete the created hooks and send the error message to user + if (counter === 4) { + await botNotifyCurrentUser( this.read, - this.http, - team_id, + this.modify, + user, + room, + response.data.reason + ); + // todo: for now figma gives a reason and says to delete a webhook but modify it to say at least 5 webhooks. + } else { + console.log( + 'error: response data error' + ); + } + } else { + await createWebhookSubscription + .createWebhookResponseHandler( + context, + response, room, user, - room_data, - event_type, - subscription, project_Ids, file_Ids, - subscriptionStorage, - teamData - ); - - if (!returnValue.success) { - return; - } - } - } - - const block = this.modify - .getCreator() - .getBlockBuilder(); - - if ( - !project_Ids?.length && - !file_Ids?.length - ) { - console.log('team'); + team_id, + event_type, + event + ) + .then(async () => { + if (counter === 4) { + const block = this.modify + .getCreator() + .getBlockBuilder(); - //todo: update this message later and add subscribed events too - block.addSectionBlock({ - text: { - text: `A new team subscriptions is created by *${user.name}*. You will start receiving notifications for updates and comments inside the team in this channel.`, - type: TextObjectType.MARKDOWN - } - }); - // create button action block - block.addActionsBlock({ - elements: [ - block.newButtonElement({ - actionId: 'view_team', - text: block.newPlainTextObject( - 'View Team Inside Figma' - ), - url: `https://www.figma.com/files/team/${team_id}` - }) - ] - }); - } else if ( - project_Ids?.length && - !file_Ids?.length - ) { - // get project details from figma - // todo: send all the projects details inside channel - block.addSectionBlock({ - text: { - text: `New subscriptions for projects is created by *${user.name}*. You will start receiving notifications for updates and comments inside the project in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, - type: TextObjectType.MARKDOWN - } - }); + if ( + !project_Ids?.length && + !file_Ids?.length + ) { + //todo: update this message later and add subscribed events too + block.addSectionBlock({ + text: { + text: `A new team subscriptions is created by *${user.name}*. You will start receiving notifications for updates and comments inside the team in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, + type: TextObjectType.MARKDOWN + } + }); + // create button action block + block.addActionsBlock({ + elements: [ + block.newButtonElement( + { + actionId: + 'view_team', + text: block.newPlainTextObject( + 'View Team Inside Figma' + ), + url: `https://www.figma.com/files/team/${team_id}` + } + ) + ] + }); + } else if ( + project_Ids?.length && + !file_Ids?.length + ) { + // get project details from figma + // todo: send all the projects details inside channel + block.addSectionBlock({ + text: { + text: `New subscriptions for projects is created by *${user.name}*. You will start receiving notifications for updates and comments inside the project in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, + type: TextObjectType.MARKDOWN + } + }); - block.addActionsBlock({ - elements: [ - block.newButtonElement({ - actionId: 'view_projects', - text: block.newPlainTextObject( - 'View Projects Inside Figma' - ), - url: `https://www.figma.com/files/team/${team_id}` - }) - ] - }); - } else if ( - !project_Ids?.length && - file_Ids?.length - ) { - block.addSectionBlock({ - text: { - text: `New subscriptions for Files is created by *${user.name}*. You will start receiving notifications for updates and comments from these files in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, - type: TextObjectType.MARKDOWN - } - }); - block.addActionsBlock({ - elements: [ - block.newButtonElement({ - actionId: 'view_files', - text: block.newPlainTextObject( - 'View files Inside Figma' - ), - url: `https://www.figma.com/files/team/${team_id}` - }) - ] - }); + block.addActionsBlock({ + elements: [ + block.newButtonElement( + { + actionId: + 'view_projects', + text: block.newPlainTextObject( + 'View Projects Inside Figma' + ), + url: `https://www.figma.com/files/team/${team_id}` + } + ) + ] + }); + } else if ( + !project_Ids?.length && + file_Ids?.length + ) { + block.addSectionBlock({ + text: { + text: `New subscriptions for Files is created by *${user.name}*. You will start receiving notifications for updates and comments from these files in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, + type: TextObjectType.MARKDOWN + } + }); + block.addActionsBlock({ + elements: [ + block.newButtonElement( + { + actionId: + 'view_files', + text: block.newPlainTextObject( + 'View files Inside Figma' + ), + url: `https://www.figma.com/files/team/${team_id}` + } + ) + ] + }); + } + await botMessageChannel( + this.read, + this.modify, + room, + block + ); + } + return; + }) + .catch(async () => { + if (count === 4) { + await botNotifyCurrentUser( + this.read, + this.modify, + user, + room, + 'error subscribing to the file please try again' + ); + } + return; + }); } - await botMessageChannel( + counter++; + }) + .catch((e) => { + botNotifyCurrentUser( this.read, this.modify, + user, room, - block + `Error Subscribing to file from figma, ${e.message}` ); - } else { - // 1 - no subscription found' - let counter = 0; - [ - events.COMMENT, - events.DELETE, - events.LIBRARY_PUBLISHED, - events.UPDATE, - events.VERSION_UPDATE - ].map(async (event) => { - // our main logic is to hook into figma 5 times for every event type - const data = { - event_type: event, - team_id: team_id, - endpoint: url, - passcode: room.id, // Send room id as passcode - description: room.id - }; - // we send request to figma webhook to create a hook for every event ( runs 5 times ) - await postRequest( - this.read, - context, - this.http, - 'https://api.figma.com/v2/webhooks', - data - ) - .then(async (response) => { - // when a hook is created successfully we save it in db - if ( - response.data.error === true - ) { - // todo : manage this logic in a better way which covers all the edge cases. If for any request there is a error response then delete the created hooks and send the error message to user - if (counter === 4) { - await botNotifyCurrentUser( - this.read, - this.modify, - user, - room, - response.data.reason - ); - // todo: for now figma gives a reason and says to delete a webhook but modify it to say at least 5 webhooks. - } else { - console.log( - 'error: response data error' - ); - } - } else { - await createWebhookSubscription - .createWebhookResponseHandler( - context, - response, - room, - user, - project_Ids, - file_Ids, - team_id, - event_type, - event - ) - .then(async () => { - if (counter === 4) { - const block = - this.modify - .getCreator() - .getBlockBuilder(); - - if ( - !project_Ids?.length && - !file_Ids?.length - ) { - //todo: update this message later and add subscribed events too - block.addSectionBlock( - { - text: { - text: `A new team subscriptions is created by *${user.name}*. You will start receiving notifications for updates and comments inside the team in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, - type: TextObjectType.MARKDOWN - } - } - ); - // create button action block - block.addActionsBlock( - { - elements: - [ - block.newButtonElement( - { - actionId: - 'view_team', - text: block.newPlainTextObject( - 'View Team Inside Figma' - ), - url: `https://www.figma.com/files/team/${team_id}` - } - ) - ] - } - ); - } else if ( - project_Ids?.length && - !file_Ids?.length - ) { - // get project details from figma - // todo: send all the projects details inside channel - block.addSectionBlock( - { - text: { - text: `New subscriptions for projects is created by *${user.name}*. You will start receiving notifications for updates and comments inside the project in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, - type: TextObjectType.MARKDOWN - } - } - ); - - block.addActionsBlock( - { - elements: - [ - block.newButtonElement( - { - actionId: - 'view_projects', - text: block.newPlainTextObject( - 'View Projects Inside Figma' - ), - url: `https://www.figma.com/files/team/${team_id}` - } - ) - ] - } - ); - } else if ( - !project_Ids?.length && - file_Ids?.length - ) { - block.addSectionBlock( - { - text: { - text: `New subscriptions for Files is created by *${user.name}*. You will start receiving notifications for updates and comments from these files in this channel. If you have not connected your figma account to rocket chat please connect by typing \`/figma connect\``, - type: TextObjectType.MARKDOWN - } - } - ); - block.addActionsBlock( - { - elements: - [ - block.newButtonElement( - { - actionId: - 'view_files', - text: block.newPlainTextObject( - 'View files Inside Figma' - ), - url: `https://www.figma.com/files/team/${team_id}` - } - ) - ] - } - ); - } - await botMessageChannel( - this.read, - this.modify, - room, - block - ); - } - return; - }) - .catch(async () => { - if (count === 4) { - await botNotifyCurrentUser( - this.read, - this.modify, - user, - room, - 'error subscribing to the file please try again' - ); - } - return; - }); - } - counter++; - }) - .catch((e) => { - botNotifyCurrentUser( - this.read, - this.modify, - user, - room, - `Error Subscribing to file from figma, ${e.message}` - ); - }) - .finally(() => count++); - }); - } - }) - .catch(async (err) => { - return await botNotifyCurrentUser( - this.read, - this.modify, - user, - room, - `Error Subscribing to file from figma, ${err.message}` - ); - }) - .finally(() => { - console.log( - 'done: Subscription created successfully' - ); - }); + }) + .finally(() => count++); + }); } - } - } + }) + .catch(async (err) => { + return await botNotifyCurrentUser( + this.read, + this.modify, + user, + room, + `Error Subscribing to file from figma, ${err.message}` + ); + }) + .finally(() => { + console.log('done: Subscription created successfully'); + }); } catch (error) { await botNotifyCurrentUser( this.read,