LoginSignup
4
3

More than 1 year has passed since last update.

【GAS】+【Slack api】で多機能なQ&Aボットを作ってみた

Last updated at Posted at 2022-12-20

はじめに

私は株式会社アドベンチャーにて、skyticketを開発しています。
今回、ナレッジ共有を目的にGAS(Google Apps Script)とSlack apiを利用した【Q&Aボット】を作成してみました。

作成背景

人は忘れる生き物です。
職場でナレッジ資料を作り「ここにあるからね!」とURL周知しても
数日後に「〇〇の件どこに書いてある?」という質問が必ずやってきます。
そして数ヶ月後、ナレッジ共有した本人も忘れて探し始めます。

人は怠ける生き物です。
「資料があそこにある」と分かっていても、つい知ってそうな人に質問してしまうものです。
「知らないだろうな」と思いつつダメ元で聞く事も。
探しているものは、たいていどこかで見聞きした覚えのある資料だったりします。
要するに調査の『取っ掛かり』をくれる相談相手が欲しいのです。

【解決策】
そこでQ&Aボットの出番です。
ボットは忘れません。
嫌な顔ひとつせず24時間相談に応えてくれます。
GASならベースとなるスプレッドシートを共有すれば誰でも簡単に質問・回答を追加できます。
人は「ボットに聞いてくれ」のワンフレーズだけ覚えればいいのです。

機能説明

1:質問に対する回答機能
Slackでキーワードを投稿すると、対応する情報を返してくれる。
(質問と回答が 1 対 1
Usshi 0652.png
2:抽選機能
「メンテナンス当番を抽選で決めたい」場合などご自由に。
(質問と回答が 1 対 n
Usshi 0655.png
3:応援機能
ネガティブなワードを投稿するとミス◯ル風の励ましポエムをランダムで返してくれます。
暇を持て余して作りました。ピンとこない方は無視してください。
(質問と回答が n 対 n
Usshi 0655.png
障害発生してしんどい、もうだめ、という職場環境でないことを忘れずに補足。
むしろホワイトです(なぜこの機能を作ったのだろうか)。

作成手順

▼参考
Slack+GASでQ&Aボットを作ってみた
【GAS】スプレッドシートのランダムな行データを取得してツイート(GAS×TwitterBot⑤)
【GAS】Google Apps ScriptでSpreadSheetのシートを指定する方法を解説

▼素材
いらすとや

Slack apiの作成

こちらでメッセージ投稿用のSlack apiを作成します。

ログインしたらCreate New Appを押下。
slack api.png
From an app manifestを選択。
Create an app.png
ボットを追加するSlackワークスペースを選択。
Pick a workspace to develop your app.png
Nextを押下。
Enter app manifest below BETA.png
Createを押下。
Review summary & create your app.png
Incoming Webhooksを押下。
Building Apps for Slack.png
メッセージ投稿用のWebhook URLを取得するため
まずはOnを押下。
Incoming Webhooks.png
Add New Webhook to Workspaceを押下。
If you deactivate incoming wethooks, new Wethook URLs will not be generated when.png
投稿先のチャンネルを求められるので、選択して「許可する」を押下。
Pasted Graphic 18.png
アプリ作成時に使用するためWebhook URLをCopyして控えておく。
Webhook URLs for Your Workspace.png
こちらでSlack上の表示名を変更可能。
Features.png

GASでボットアプリを作成

googleスプレッドシートをベースにGASでボットアプリを作成します。

スプレッドシートのブックを作成し、機能別にシートを作成する。
Pasted Graphic 21.png
質問に対する回答機能用シート
質問キーワードをA列、対応する回答をB列に入力。
Poem Answer.png
抽選機能用シート
抽選対象をA列に入力。
Pasted Graphic 25.png
応援機能用シート
2つ作ります。
▼1つ目のシート
応援対象となるネガティブキーワードをA列に入力。
Poem Answer.png
▼2つ目のシート
応援メッセージ用のポエムをA列に入力。
見返すと目も当てられない内容だったので黒塗りしました。
皆さんなりの応援メッセージを募集しております。
Pasted Graphic 60.png

シートを作成したらいよいよGASでの作業。

スプレッドシートの拡張機能からApps Scriptを選択。
Pasted Graphic 26.png
コードを記述。
Pasted Graphic 27.png

コード

var HTTP_METHOD_POST = 'post';
var LOTTERY_KEY_WORD = '今週の当番';
var TARGET_COLUMN_NUM_1 = 1;
var TARGET_COLUMN_NUM_2 = 2;
var UNKNOWN_ANSWER_TEXT = '分かりません:coffee:';

function doPost(e){

  // 疎通確認
  const params = JSON.parse(e.postData.getDataAsString());
  if('challenge' in params){
    return ContentService.createTextOutput(params.challenge);
  }

  // Botの投稿に反応しない
  if('subtype' in params.event) {
    return;
  }

  let contents = "";
  let postOption = "";
  if('text' in params.event) {
    let answer;

    const activeSpreadSheet = SpreadsheetApp.getActiveSpreadsheet();
    let sheet = activeSpreadSheet.getSheetByName('Poem_Keyword');
    // 受信メッセージが応援対象であるか判定
    const pkSearchRes = vlookup(params.event.text, sheet, TARGET_COLUMN_NUM_1);

    if(pkSearchRes !== UNKNOWN_ANSWER_TEXT){
    // 応援対象の場合はランダムでポエム返却
      sheet = activeSpreadSheet.getSheetByName('Poem_Answer');
      answer = getRandomRow(sheet, TARGET_COLUMN_NUM_1) + ' :coffee:';
    }else if(params.event.text === LOTTERY_KEY_WORD){
    // 抽選キーワードを受信した場合はランダムで結果返却
      sheet = activeSpreadSheet.getSheetByName('Member');
      answer = ':tada: ' + getRandomRow(sheet, TARGET_COLUMN_NUM_1) + ' :confetti_ball:';
    }else{
    // 上記に当てはまらない場合は質問とみなし対応する回答を返却
      sheet = activeSpreadSheet.getSheetByName('Q&A');
      answer = vlookup(params.event.text, sheet, TARGET_COLUMN_NUM_2);
    }

    const userName = params.event.user;
    contents = `<@${userName}> \n` + answer; 
  }

  postOption = makeHttpOption(HTTP_METHOD_POST, contents);

  // メッセージ投稿
  UrlFetchApp.fetch("SlackのWebhookURL", postOption);
}

// エクセルvlookupのような関数
function vlookup(value, sheet, columnNum) {
  let returnValue = UNKNOWN_ANSWER_TEXT;
  for (let i = 1; i <= sheet.getLastRow(); i++) {
    if(value === sheet.getRange(i,1).getValue()){
      returnValue = sheet.getRange(i,columnNum).getValue();
      break;
    }
  }    
  return returnValue;
}

// シートと列を指定してランダムに行を抽選
function getRandomRow(sheet, columnNum) {
  const lastRow = sheet.getLastRow();
  const rowNum = Math.ceil(Math.random() * (lastRow));  
  const rowTxt = sheet.getRange(rowNum, columnNum).getValue()
  return rowTxt;
}

// HTTPオプション作成
function makeHttpOption(httpMethod, contents) {
  const optionData =
  {
    "method" : httpMethod,
    "contentType" : "application/json",
    "payload" : JSON.stringify(
      {
        "text" : contents,
        link_names: 1
      }
    )
  };
  return optionData;
}

画面右上のボタンからデプロイ。
Pasted Graphic 28.png
モーダルが表示されるのでウェブアプリを選択。
Pasted Graphic 29.png
以下のように入力・選択しデプロイ実行。
first deploy.png
アクセスを承認。
Pasted Graphic 31.png
Googleアカウントを選択すると
アクセス許可を求められる。
Google hasn't verified this app.png
デプロイが完了したらウェブアプリURLをコピーする。
Pasted Graphic 44.png

Slack api ・ Slackに作成したボットアプリを追加

ここから再びSlack側の作業です。

Slack apiのEvent Subscriptions画面で以下の設定を行い、Save Changesを押下。

  • Enable Events:On
  • Request URL:GASアプリのデプロイ時にコピーしたURL
  • Subscribe to bot events:message.channels
    Event Subscriptions.png
    画面上部に権限変更の注意文言が表示されたらreinstall your appを押下。
    Pasted Graphic 46.png
    メッセージ投稿先にするSlackチャンネルを指定して許可する。
    Slack 7.png
    指定チャンネルのインテグレーションに作成したAPIが追加される。
    Usshi 0833.png
    Slackチャンネル設定画面のインテグレーションでアプリを追加するを押下。
    Pasted Graphic 50.png
    作成したアプリの追加を押下。
    Demo App.png
    チャンネルにボットがアカウント追加されたら完成!
    Pasted Graphic 53.png

最後に

名曲を聴き、くだらないポエムを考えながら思いました。
人の心を動かしてこそ、優れたプロダクトなのだと。
便利なだけでは記憶に残りません。
そう、応援機能がキモでした。何事もセンスです。

最後に真面目な事を言うと、
キーワードの「曖昧検索」に対応するのを忘れていました。
気が向いたら更新します。

読んでくださった皆さま、ありがとうございました。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3