Ver Fonte

Moved search command block actions

hakumizuki há 4 anos atrás
pai
commit
b23c25b316

+ 1 - 16
src/server/routes/apiv3/slack-integration.js

@@ -129,22 +129,7 @@ module.exports = (crowi) => {
 
 
   const handleBlockActions = async(client, payload) => {
-    const { action_id: actionId } = payload.actions[0];
-
-    switch (actionId) {
-      // GW-6844 merge後に移行終了
-      case 'search:showNextResults': {
-        const parsedValue = JSON.parse(payload.actions[0].value);
-
-        const { body, args, offset } = parsedValue;
-        const newOffset = offset + 10;
-        await crowi.slackBotService.showEphemeralSearchResults(client, body, args, newOffset);
-        break;
-      }
-      default:
-        await crowi.slackBotService.handleBlockActions(client, payload);
-        break;
-    }
+    await crowi.slackBotService.handleBlockActions(client, payload);
   };
 
   const handleViewSubmission = async(client, payload) => {

+ 165 - 9
src/server/service/slack-command-handler/search.js

@@ -28,8 +28,8 @@ module.exports = (crowi) => {
       throw new Error('/growi command:search: Failed to search');
     }
 
-    const appUrl = this.crowi.appService.getSiteUrl();
-    const appTitle = this.crowi.appService.getAppTitle();
+    const appUrl = crowi.appService.getSiteUrl();
+    const appTitle = crowi.appService.getAppTitle();
 
     const {
       pages, offset, resultsTotal,
@@ -82,7 +82,7 @@ module.exports = (crowi) => {
           },
           accessory: {
             type: 'button',
-            action_id: 'search:shareSingleSearchResult',
+            action_id: 'search:shareSinglePageResult',
             text: {
               type: 'plain_text',
               text: 'Share',
@@ -120,7 +120,7 @@ module.exports = (crowi) => {
             text: 'Dismiss',
           },
           style: 'danger',
-          action_id: 'dismissSearchResults',
+          action_id: 'search:dismissSearchResults',
         },
       ],
     };
@@ -133,7 +133,7 @@ module.exports = (crowi) => {
             type: 'plain_text',
             text: 'Next',
           },
-          action_id: 'showNextResults',
+          action_id: 'search:showNextResults',
           value: JSON.stringify({ offset, body, args }),
         },
       );
@@ -158,7 +158,7 @@ module.exports = (crowi) => {
           markdownSectionBlock(err.toString()),
         ],
       });
-      throw new Error(err);
+      throw new Error(err.message);
     }
   };
 
@@ -169,8 +169,8 @@ module.exports = (crowi) => {
   handler.shareSinglePageResult = async function(client, payload) {
     const { channel, user, actions } = payload;
 
-    const appUrl = this.crowi.appService.getSiteUrl();
-    const appTitle = this.crowi.appService.getAppTitle();
+    const appUrl = crowi.appService.getSiteUrl();
+    const appTitle = crowi.appService.getAppTitle();
 
     const channelId = channel.id;
     const action = actions[0]; // shareSinglePage action must have button action
@@ -199,6 +199,162 @@ module.exports = (crowi) => {
     });
   };
 
+  handler.showNextResults = async function(client, payload) {
+    const parsedValue = JSON.parse(payload.actions[0].value);
+
+    const { body, args, offsetNum } = parsedValue;
+    const newOffsetNum = offsetNum + 10;
+    let searchResult;
+    try {
+      searchResult = await this.retrieveSearchResults(client, body, args, newOffsetNum);
+    }
+    catch (err) {
+      logger.error('Failed to get search results.', err);
+      await client.chat.postEphemeral({
+        channel: body.channel_id,
+        user: body.user_id,
+        text: 'Failed To Search',
+        blocks: [
+          markdownSectionBlock('*Failed to search.*\n Hint\n `/growi search [keyword]`'),
+        ],
+      });
+      throw new Error('/growi command:search: Failed to search');
+    }
+
+    const appUrl = crowi.appService.getSiteUrl();
+    const appTitle = crowi.appService.getAppTitle();
+
+    const {
+      pages, offset, resultsTotal,
+    } = searchResult;
+
+    const keywords = this.getKeywords(args);
+
+
+    let searchResultsDesc;
+
+    switch (resultsTotal) {
+      case 1:
+        searchResultsDesc = `*${resultsTotal}* page is found.`;
+        break;
+
+      default:
+        searchResultsDesc = `*${resultsTotal}* pages are found.`;
+        break;
+    }
+
+
+    const contextBlock = {
+      type: 'context',
+      elements: [
+        {
+          type: 'mrkdwn',
+          text: `keyword(s) : *"${keywords}"*  |  Current: ${offset + 1} - ${offset + pages.length}  |  Total ${resultsTotal} pages`,
+        },
+      ],
+    };
+
+    const now = new Date();
+    const blocks = [
+      markdownSectionBlock(`:mag: <${decodeURI(appUrl)}|*${appTitle}*>\n${searchResultsDesc}`),
+      contextBlock,
+      { type: 'divider' },
+      // create an array by map and extract
+      ...pages.map((page) => {
+        const { path, updatedAt, commentCount } = page;
+        // generate URL
+        const url = new URL(path, appUrl);
+        const { href, pathname } = url;
+
+        return {
+          type: 'section',
+          text: {
+            type: 'mrkdwn',
+            text: `${this.appendSpeechBaloon(`*${this.generatePageLinkMrkdwn(pathname, href)}*`, commentCount)}`
+              + `\n    Last updated: ${this.generateLastUpdateMrkdwn(updatedAt, now)}`,
+          },
+          accessory: {
+            type: 'button',
+            action_id: 'shareSingleSearchResult',
+            text: {
+              type: 'plain_text',
+              text: 'Share',
+            },
+            value: JSON.stringify({ page, href, pathname }),
+          },
+        };
+      }),
+      { type: 'divider' },
+      contextBlock,
+    ];
+
+    // DEFAULT show "Share" button
+    // const actionBlocks = {
+    //   type: 'actions',
+    //   elements: [
+    //     {
+    //       type: 'button',
+    //       text: {
+    //         type: 'plain_text',
+    //         text: 'Share',
+    //       },
+    //       style: 'primary',
+    //       action_id: 'shareSearchResults',
+    //     },
+    //   ],
+    // };
+    const actionBlocks = {
+      type: 'actions',
+      elements: [
+        {
+          type: 'button',
+          text: {
+            type: 'plain_text',
+            text: 'Dismiss',
+          },
+          style: 'danger',
+          action_id: 'search:dismissSearchResults',
+        },
+      ],
+    };
+    // show "Next" button if next page exists
+    if (resultsTotal > offset + PAGINGLIMIT) {
+      actionBlocks.elements.unshift(
+        {
+          type: 'button',
+          text: {
+            type: 'plain_text',
+            text: 'Next',
+          },
+          action_id: 'search:showNextResults',
+          value: JSON.stringify({ offset, body, args }),
+        },
+      );
+    }
+    blocks.push(actionBlocks);
+
+    try {
+      await client.chat.postEphemeral({
+        channel: body.channel_id,
+        user: body.user_id,
+        text: 'Successed To Search',
+        blocks,
+      });
+    }
+    catch (err) {
+      logger.error('Failed to post ephemeral message.', err);
+      await client.chat.postEphemeral({
+        channel: body.channel_id,
+        user: body.user_id,
+        text: 'Failed to post ephemeral message.',
+        blocks: [
+          markdownSectionBlock(err.toString()),
+        ],
+      });
+      throw new Error(err.message);
+    }
+  };
+
   handler.dismissSearchResults = async function(client, payload) {
     const { response_url: responseUrl } = payload;
 
@@ -223,7 +379,7 @@ module.exports = (crowi) => {
 
     const keywords = this.getKeywords(args);
 
-    const { searchService } = this.crowi;
+    const { searchService } = crowi;
     const options = { limit: 10, offset };
     const results = await searchService.searchKeyword(keywords, null, {}, options);
     const resultsTotal = results.meta.total;

+ 9 - 2
src/server/service/slackbot.js

@@ -66,13 +66,20 @@ class SlackBotService extends S2sMessageHandlable {
    * Handle /commands endpoint
    */
   async handleCommand(command, client, body, ...opt) {
-    const module = `./slack-command-handler/${command}`;
+    let module;
+    try {
+      module = `./slack-command-handler/${command}`;
+    }
+    catch (err) {
+      this.notCommand(client, body);
+    }
+
     try {
       const handler = require(module)(this.crowi);
       await handler.handleCommand(client, body, ...opt);
     }
     catch (err) {
-      this.notCommand(client, body);
+      throw err;
     }
   }