| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- import type {
- GrowiCommand,
- GrowiCommandProcessor,
- GrowiInteractionProcessor,
- InteractionHandledResult,
- } from '@growi/slack';
- import {
- inputBlock,
- inputSectionBlock,
- markdownHeaderBlock,
- markdownSectionBlock,
- } from '@growi/slack/dist/utils/block-kit-builder';
- import { InteractionPayloadAccessor } from '@growi/slack/dist/utils/interaction-payload-accessor';
- import { getInteractionIdRegexpFromCommandName } from '@growi/slack/dist/utils/payload-interaction-id-helpers';
- import { respond } from '@growi/slack/dist/utils/response-url';
- import { AuthorizeResult } from '@slack/oauth';
- import {
- Block,
- ConversationsSelect,
- LogLevel,
- WebClient,
- } from '@slack/web-api';
- import { Inject, Service } from '@tsed/di';
- import { InstallationRepository } from '~/repositories/installation';
- import { OrderRepository } from '~/repositories/order';
- import loggerFactory from '~/utils/logger';
- import { InvalidUrlError } from '../models/errors';
- const logger = loggerFactory('slackbot-proxy:services:RegisterService');
- const isProduction = process.env.NODE_ENV === 'production';
- const isOfficialMode = process.env.OFFICIAL_MODE === 'true';
- export type RegisterCommandBody = {
- // eslint-disable-next-line camelcase
- trigger_id: string;
- // eslint-disable-next-line camelcase
- channel_name: string;
- };
- @Service()
- export class RegisterService
- implements
- GrowiCommandProcessor<RegisterCommandBody>,
- GrowiInteractionProcessor<void>
- {
- @Inject()
- orderRepository: OrderRepository;
- @Inject()
- installationRepository: InstallationRepository;
- shouldHandleCommand(growiCommand: GrowiCommand): boolean {
- return growiCommand.growiCommandType === 'register';
- }
- async processCommand(
- growiCommand: GrowiCommand,
- authorizeResult: AuthorizeResult,
- context: RegisterCommandBody,
- ): Promise<void> {
- const { botToken } = authorizeResult;
- const client = new WebClient(botToken, {
- logLevel: isProduction ? LogLevel.DEBUG : LogLevel.INFO,
- });
- const conversationsSelectElement: ConversationsSelect = {
- action_id: 'conversation',
- type: 'conversations_select',
- response_url_enabled: true,
- default_to_current_conversation: true,
- };
- await client.views.open({
- trigger_id: context.trigger_id,
- view: {
- type: 'modal',
- callback_id: 'register:register',
- title: {
- type: 'plain_text',
- text: 'Register Credentials',
- },
- submit: {
- type: 'plain_text',
- text: 'Submit',
- },
- close: {
- type: 'plain_text',
- text: 'Close',
- },
- private_metadata: JSON.stringify({ channel: context.channel_name }),
- blocks: [
- inputBlock(
- conversationsSelectElement,
- 'conversation',
- 'Channel to which you want to add',
- ),
- inputSectionBlock(
- 'growiUrl',
- 'GROWI domain',
- 'contents_input',
- false,
- 'https://example.com',
- ),
- inputSectionBlock(
- 'tokenPtoG',
- 'Access Token Proxy to GROWI',
- 'contents_input',
- false,
- 'jBMZvpk.....',
- ),
- inputSectionBlock(
- 'tokenGtoP',
- 'Access Token GROWI to Proxy',
- 'contents_input',
- false,
- 'sdg15av.....',
- ),
- ],
- },
- });
- }
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
- shouldHandleInteraction(
- interactionPayloadAccessor: InteractionPayloadAccessor,
- ): boolean {
- const { actionId, callbackId } =
- interactionPayloadAccessor.getActionIdAndCallbackIdFromPayLoad();
- const registerRegexp: RegExp =
- getInteractionIdRegexpFromCommandName('register');
- return registerRegexp.test(actionId) || registerRegexp.test(callbackId);
- }
- async processInteraction(
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
- authorizeResult: AuthorizeResult,
- interactionPayload: any,
- interactionPayloadAccessor: InteractionPayloadAccessor,
- ): Promise<InteractionHandledResult<void>> {
- const interactionHandledResult: InteractionHandledResult<void> = {
- isTerminated: false,
- };
- if (!this.shouldHandleInteraction(interactionPayloadAccessor))
- return interactionHandledResult;
- interactionHandledResult.result = await this.handleRegisterInteraction(
- authorizeResult,
- interactionPayload,
- interactionPayloadAccessor,
- );
- interactionHandledResult.isTerminated = true;
- return interactionHandledResult as InteractionHandledResult<void>;
- }
- async handleRegisterInteraction(
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
- authorizeResult: AuthorizeResult,
- interactionPayload: any,
- interactionPayloadAccessor: InteractionPayloadAccessor,
- ): Promise<void> {
- try {
- await this.insertOrderRecord(authorizeResult, interactionPayloadAccessor);
- } catch (err) {
- if (err instanceof InvalidUrlError) {
- logger.error('Failed to register:\n', err);
- await respond(interactionPayloadAccessor.getResponseUrl(), {
- text: 'Invalid URL',
- blocks: [markdownSectionBlock('Please enter a valid URL')],
- });
- return;
- }
- logger.error('Error occurred while insertOrderRecord:\n', err);
- }
- await this.notifyServerUriToSlack(interactionPayloadAccessor);
- }
- async insertOrderRecord(
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
- authorizeResult: AuthorizeResult,
- interactionPayloadAccessor: InteractionPayloadAccessor,
- ): Promise<void> {
- const inputValues = interactionPayloadAccessor.getStateValues();
- const growiUrl = inputValues.growiUrl.contents_input.value;
- const tokenPtoG = inputValues.tokenPtoG.contents_input.value;
- const tokenGtoP = inputValues.tokenGtoP.contents_input.value;
- try {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const url = new URL(growiUrl);
- } catch (error) {
- throw new InvalidUrlError(growiUrl);
- }
- const installationId =
- authorizeResult.enterpriseId || authorizeResult.teamId;
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const installation =
- await this.installationRepository.findByTeamIdOrEnterpriseId(
- installationId!,
- );
- this.orderRepository.save({
- installation,
- growiUrl,
- tokenPtoG,
- tokenGtoP,
- });
- }
- async notifyServerUriToSlack(
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
- interactionPayloadAccessor: InteractionPayloadAccessor,
- ): Promise<void> {
- const serverUri = process.env.SERVER_URI;
- const responseUrl = interactionPayloadAccessor.getResponseUrl();
- const blocks: Block[] = [];
- if (isOfficialMode) {
- blocks.push(
- markdownHeaderBlock(
- ':white_check_mark: 1. Install Official bot to Slack',
- ),
- );
- blocks.push(
- markdownHeaderBlock(
- ':white_check_mark: 2. Register for GROWI Official Bot Proxy Service',
- ),
- );
- blocks.push(
- markdownSectionBlock(
- 'The request has been successfully accepted. However, registration has *NOT been completed* yet.',
- ),
- );
- blocks.push(markdownHeaderBlock(':arrow_right: 3. Test Connection'));
- blocks.push(
- markdownSectionBlock(
- '*Test Connection* to complete the registration in your GROWI.',
- ),
- );
- blocks.push(
- markdownHeaderBlock(':white_large_square: 4. (Opt) Manage Permission'),
- );
- blocks.push(
- markdownSectionBlock('Modify permission settings if you need.'),
- );
- await respond(responseUrl, {
- text: 'Proxy URL',
- blocks,
- });
- return;
- }
- blocks.push(markdownHeaderBlock(':white_check_mark: 1. Create Bot'));
- blocks.push(
- markdownHeaderBlock(':white_check_mark: 2. Install bot to Slack'),
- );
- blocks.push(
- markdownHeaderBlock(
- ':white_check_mark: 3. Register for your GROWI Custom Bot Proxy',
- ),
- );
- blocks.push(
- markdownSectionBlock(
- 'The request has been successfully accepted. However, registration has *NOT been completed* yet.',
- ),
- );
- blocks.push(markdownHeaderBlock(':arrow_right: 4. Set Proxy URL on GROWI'));
- blocks.push(
- markdownSectionBlock(
- 'Please enter and update the following Proxy URL to slack bot setting form in your GROWI',
- ),
- );
- blocks.push(markdownSectionBlock(`Proxy URL: ${serverUri}`));
- blocks.push(markdownHeaderBlock(':arrow_right: 5. Test Connection'));
- blocks.push(
- markdownSectionBlock(
- 'And *Test Connection* to complete the registration in your GROWI.',
- ),
- );
- blocks.push(
- markdownHeaderBlock(':white_large_square: 6. (Opt) Manage Permission'),
- );
- blocks.push(
- markdownSectionBlock('Modify permission settings if you need.'),
- );
- await respond(responseUrl, {
- text: 'Proxy URL',
- blocks,
- });
- return;
- }
- }
|