| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- import {
- getInteractionIdRegexpFromCommandName,
- GrowiCommand, GrowiCommandProcessor, GrowiInteractionProcessor,
- InteractionHandledResult, markdownSectionBlock, replaceOriginal, respond, InteractionPayloadAccessor,
- } from '@growi/slack';
- import { AuthorizeResult } from '@slack/oauth';
- import { Inject, Service } from '@tsed/di';
- import { Installation } from '~/entities/installation';
- import { Relation } from '~/entities/relation';
- import { InstallationRepository } from '~/repositories/installation';
- import { RelationRepository } from '~/repositories/relation';
- import loggerFactory from '~/utils/logger';
- const logger = loggerFactory('slackbot-proxy:services:UnregisterService');
- export type SelectGrowiCommandBody = {
- growiUrisForSingleUse: string[],
- }
- type SelectValue = {
- growiCommand: GrowiCommand,
- growiUri: any,
- }
- type SendCommandBody = {
- // eslint-disable-next-line camelcase
- trigger_id: string,
- // eslint-disable-next-line camelcase
- channel_id: string,
- // eslint-disable-next-line camelcase
- channel_name: string,
- }
- export type SelectedGrowiInformation = {
- relation: Relation,
- growiCommand: GrowiCommand,
- sendCommandBody: SendCommandBody,
- }
- @Service()
- export class SelectGrowiService implements GrowiCommandProcessor<SelectGrowiCommandBody | null>, GrowiInteractionProcessor<SelectedGrowiInformation> {
- @Inject()
- relationRepository: RelationRepository;
- @Inject()
- installationRepository: InstallationRepository;
- private generateGrowiSelectValue(growiCommand: GrowiCommand, growiUri: string): SelectValue {
- return {
- growiCommand,
- growiUri,
- };
- }
- shouldHandleCommand(): boolean {
- // TODO: consider to use the default supported commands for single use
- return true;
- }
- async processCommand(
- growiCommand: GrowiCommand, authorizeResult: AuthorizeResult, context: SelectGrowiCommandBody,
- ): Promise<void> {
- const growiUrls = context.growiUrisForSingleUse;
- const chooseSection = growiUrls.map((growiUri) => {
- const value = this.generateGrowiSelectValue(growiCommand, growiUri);
- return ({
- type: 'section',
- text: {
- type: 'mrkdwn',
- text: growiUri,
- },
- accessory: {
- type: 'button',
- action_id: 'select_growi:select_growi',
- text: {
- type: 'plain_text',
- text: 'Choose',
- },
- value: JSON.stringify(value),
- },
- });
- });
- return respond(growiCommand.responseUrl, {
- blocks: [
- {
- type: 'header',
- text: {
- type: 'plain_text',
- text: 'Select target GROWI',
- },
- },
- {
- type: 'context',
- elements: [
- {
- type: 'mrkdwn',
- text: `Request: \`/growi ${growiCommand.text}\` to:`,
- },
- ],
- },
- {
- type: 'divider',
- },
- ...chooseSection,
- ],
- });
- }
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
- shouldHandleInteraction(interactionPayloadAccessor: InteractionPayloadAccessor): boolean {
- const { actionId, callbackId } = interactionPayloadAccessor.getActionIdAndCallbackIdFromPayLoad();
- const registerRegexp: RegExp = getInteractionIdRegexpFromCommandName('select_growi');
- 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<SelectedGrowiInformation>> {
- const interactionHandledResult: InteractionHandledResult<SelectedGrowiInformation> = {
- isTerminated: false,
- };
- if (!this.shouldHandleInteraction(interactionPayloadAccessor)) return interactionHandledResult;
- const selectGrowiInformation = await this.handleSelectInteraction(authorizeResult, interactionPayload, interactionPayloadAccessor);
- if (selectGrowiInformation != null) {
- interactionHandledResult.result = selectGrowiInformation;
- }
- interactionHandledResult.isTerminated = false;
- return interactionHandledResult as InteractionHandledResult<SelectedGrowiInformation>;
- }
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
- async handleSelectInteraction(
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
- authorizeResult: AuthorizeResult, interactionPayload: any, interactionPayloadAccessor: InteractionPayloadAccessor,
- ): Promise<SelectedGrowiInformation | null> {
- const responseUrl = interactionPayloadAccessor.getResponseUrl();
- const selectGrowiValue = interactionPayloadAccessor.firstAction()?.value;
- if (selectGrowiValue == null) {
- logger.error('Growi command failed: The first action element must have the value parameter.');
- await respond(responseUrl, {
- text: 'Growi command failed',
- blocks: [
- markdownSectionBlock('Error occurred while processing GROWI command.'),
- ],
- });
- return null;
- }
- const { growiUri, growiCommand } = JSON.parse(selectGrowiValue);
- if (growiCommand == null) {
- logger.error('Growi command failed: The first action value must have growiCommand parameter.');
- await respond(responseUrl, {
- text: 'Growi command failed',
- blocks: [
- markdownSectionBlock('Error occurred while processing GROWI command.'),
- ],
- });
- return null;
- }
- await replaceOriginal(responseUrl, {
- text: `Accepted ${growiCommand.growiCommandType} command.`,
- blocks: [
- markdownSectionBlock(`Forwarding your request *"/growi ${growiCommand.growiCommandType}"* on GROWI to ${growiUri} ...`),
- ],
- });
- const installationId = authorizeResult.enterpriseId || authorizeResult.teamId;
- let installation: Installation | undefined;
- try {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- installation = await this.installationRepository.findByTeamIdOrEnterpriseId(installationId!);
- }
- catch (err) {
- logger.error('Growi command failed: No installation found.\n', err);
- await respond(responseUrl, {
- text: 'Growi command failed',
- blocks: [
- markdownSectionBlock('Error occurred while processing GROWI command.'),
- ],
- });
- return null;
- }
- const relation = await this.relationRepository.createQueryBuilder('relation')
- .where('relation.growiUri =:growiUri', { growiUri })
- .andWhere('relation.installationId = :id', { id: installation?.id })
- .leftJoinAndSelect('relation.installation', 'installation')
- .getOne();
- if (relation == null) {
- logger.error('Growi command failed: No installation found.');
- await respond(responseUrl, {
- text: 'Growi command failed',
- blocks: [
- markdownSectionBlock('Error occurred while processing GROWI command.'),
- ],
- });
- return null;
- }
- // increment sendCommandBody
- const channel = interactionPayloadAccessor.getChannel();
- if (channel == null) {
- logger.error('Growi command failed: channel not found.');
- await respond(responseUrl, {
- text: 'Growi command failed',
- blocks: [
- markdownSectionBlock('Error occurred while processing GROWI command.'),
- ],
- });
- return null;
- }
- const sendCommandBody: SendCommandBody = {
- trigger_id: interactionPayload.trigger_id,
- channel_id: channel.id,
- channel_name: channel.name,
- };
- return {
- relation,
- growiCommand,
- sendCommandBody,
- };
- }
- }
|