Browse Source

Merge branch 'feat/growi-bot' into imprv/refactor-bolt-service

yusuketk 5 years ago
parent
commit
c7746f4475
2 changed files with 198 additions and 2 deletions
  1. 186 2
      src/server/service/bolt.js
  2. 12 0
      src/server/service/config-loader.js

+ 186 - 2
src/server/service/bolt.js

@@ -12,6 +12,12 @@ class BoltReciever {
     }
 
     let ackCalled = false;
+
+    // for verification request URL on Event Subscriptions
+    if (req.body.challenge && req.body.type) {
+      return res.send(req.body);
+    }
+
     const event = {
       body: req.body,
       ack: (response) => {
@@ -40,6 +46,7 @@ class BoltReciever {
 }
 
 const { App } = require('@slack/bolt');
+const { WebClient, LogLevel } = require('@slack/web-api');
 
 class BoltService {
 
@@ -47,8 +54,11 @@ class BoltService {
     this.crowi = crowi;
     this.receiver = new BoltReciever();
 
-    const token = process.env.SLACK_BOT_TOKEN;
-    const signingSecret = process.env.SLACK_SIGNING_SECRET;
+    const signingSecret = crowi.configManager.getConfig('crowi', 'slackbot:signingSecret');
+    const token = crowi.configManager.getConfig('crowi', 'slackbot:token');
+
+    const client = new WebClient(token, { logLevel: LogLevel.DEBUG });
+    this.client = client;
 
     if (token != null || signingSecret != null) {
       logger.debug('TwitterStrategy: setup is done');
@@ -76,6 +86,180 @@ class BoltService {
 
       await say(`${command.text}`);
     });
+
+    this.bolt.command('/growi', async({
+      command, client, body, ack,
+    }) => {
+      await ack();
+      const args = command.text.split(' ');
+      const firstArg = args[0];
+
+      switch (firstArg) {
+        case 'search':
+          this.searchResults(command, args);
+          break;
+
+        case 'create':
+          this.createModal(command, client, body);
+          break;
+
+        default:
+          this.notCommand(command);
+          break;
+      }
+    });
+
+  }
+
+  notCommand(command) {
+    logger.error('Input first arguments');
+    return this.client.chat.postEphemeral({
+      channel: command.channel_id,
+      user: command.user_id,
+      blocks: [
+        this.generateMarkdownSectionBlock('*コマンドが存在しません。*\n Hint\n `/growi [command] [keyword]`'),
+      ],
+    });
+
+  }
+
+  async searchResults(command, args) {
+    const firstKeyword = args[1];
+    if (firstKeyword == null) {
+      return this.client.chat.postEphemeral({
+        channel: command.channel_id,
+        user: command.user_id,
+        blocks: [
+          this.generateMarkdownSectionBlock('*キーワードを入力してください。*\n Hint\n `/growi search [keyword]`'),
+        ],
+      });
+    }
+
+    // remove leading 'search'.
+    args.shift();
+    const keywords = args.join(' ');
+    const { searchService } = this.crowi;
+    const option = { limit: 10 };
+    const results = await searchService.searchKeyword(keywords, null, {}, option);
+
+    // no search results
+    if (results.data.length === 0) {
+      return this.client.chat.postEphemeral({
+        channel: command.channel_id,
+        user: command.user_id,
+        blocks: [
+          this.generateMarkdownSectionBlock('*キーワードに該当するページは存在しません。*'),
+        ],
+      });
+    }
+
+    const resultPaths = results.data.map((data) => {
+      return data._source.path;
+    });
+
+    try {
+      await this.client.chat.postEphemeral({
+        channel: command.channel_id,
+        user: command.user_id,
+        blocks: [
+          this.generateMarkdownSectionBlock('検索結果 10 件'),
+          this.generateMarkdownSectionBlock(`${resultPaths.join('\n')}`),
+          {
+            type: 'actions',
+            elements: [
+              {
+                type: 'button',
+                text: {
+                  type: 'plain_text',
+                  text: '検索結果をこのチャンネルに共有する',
+                },
+                style: 'primary',
+              },
+            ],
+          },
+        ],
+      });
+    }
+    catch {
+      logger.error('Failed to get search results.');
+      await this.client.chat.postEphemeral({
+        channel: command.channel_id,
+        user: command.user_id,
+        blocks: [
+          this.generateMarkdownSectionBlock('*検索に失敗しました。*\n Hint\n `/growi search [keyword]`'),
+        ],
+      });
+    }
+  }
+
+  async createModal(command, client, body) {
+    try {
+      await client.views.open({
+        trigger_id: body.trigger_id,
+
+        view: {
+          type: 'modal',
+          callback_id: 'createPage',
+          title: {
+            type: 'plain_text',
+            text: 'Create Page',
+          },
+          submit: {
+            type: 'plain_text',
+            text: 'Submit',
+          },
+          close: {
+            type: 'plain_text',
+            text: 'Cancel',
+          },
+          blocks: [
+            this.generateMarkdownSectionBlock('ページを作成します'),
+            this.generateInputSectionBlock('path', 'Path', 'path_input', false, '/path'),
+            this.generateInputSectionBlock('contents', 'Contents', 'contents_input', true, 'Input with Markdown...'),
+          ],
+        },
+      });
+    }
+    catch {
+      logger.error('Failed to create page.');
+      await this.client.chat.postEphemeral({
+        channel: command.channel_id,
+        user: command.user_id,
+        blocks: [
+          this.generateMarkdownSectionBlock('*ページ作成に失敗しました。*\n Hint\n `/growi create`'),
+        ],
+      });
+    }
+  }
+
+  generateMarkdownSectionBlock(blocks) {
+    return {
+      type: 'section',
+      text: {
+        type: 'mrkdwn',
+        text: blocks,
+      },
+    };
+  }
+
+  generateInputSectionBlock(blockId, labelText, actionId, isMultiline, placeholder) {
+    return {
+      type: 'input',
+      block_id: blockId,
+      label: {
+        type: 'plain_text',
+        text: labelText,
+      },
+      element: {
+        type: 'plain_text_input',
+        action_id: actionId,
+        multiline: isMultiline,
+        placeholder: {
+          type: 'plain_text',
+          text: placeholder,
+        },
+      },
+    };
   }
 
 }

+ 12 - 0
src/server/service/config-loader.js

@@ -398,6 +398,18 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
     type:    TYPES.STRING,
     default: null,
   },
+  SLACK_SIGNING_SECRET: {
+    ns:      'crowi',
+    key:     'slackbot:signingSecret',
+    type:    TYPES.STRING,
+    default: null,
+  },
+  SLACK_BOT_TOKEN: {
+    ns:      'crowi',
+    key:     'slackbot:token',
+    type:    TYPES.STRING,
+    default: null,
+  },
 };
 
 class ConfigLoader {