SlackからGitHubへTILの投稿

Adways Advent Calendar 2017 3日目の記事です。

http://blog.engineer.adways.net/entry/advent_calendar_2017


こんにちは、渡部です。

Slackで個人で思ったことや考えたことを投稿するためにタイムズチャンネルを作って利用しています(参考: http://c16e.com/1511101558/)。

最近、学習したことをよく投稿しているのですが、Slackに投稿してしまうと後から読み返したりするときに散り散りになっていて読みづらいです。

今回はそんな悩める自分のために、Slackのスラッシュコマンドを使って、GitHubに今日学んだこと(Today I Learned = TIL)をまとめるようにしました。

TILについては過去の久保田くんの記事も参考にどうぞ。 (新卒必見?AWSだけで今日学んだこと(TIL)を定期配信して効率よく学習しよう - AdwaysEngineersBlog - Adwaysエンジニアブログ

使うもの

  • Google Cloud Functions

今回は初めてGoogle Cloud Functionsを使いました。

Google Cloud Functionsを使う理由としては:

  1. よくGoogle Cloudを使うため
  2. 簡易的なHTTPSサーバーとして使えるため

というものがあります。

Slackのスラッシュコマンドは/コマンド名で投稿されたら、どこかにHTTP(S)リクエストを送るというものですが、2番目に挙げた簡易的なHTTPSサーバーとして使えるというのは見事にマッチしています。

また、Slackのスラッシュコマンドのリクエストは宛先サーバーがHTTPSである必要がありますが、Google Cloud FunctionsはHTTPSに自動でなるためというのも理由です。

関数の作成

Google Cloudのコンソールの画面から、関数の作成を行います。

f:id:AdwaysEngineerBlog:20171205121033p:plain

今回は、HTTPSサーバー化したいので、トリガー(どのタイミングで関数が動くか)に「HTTPトリガー」を選択します。

ソースコード置き場として、バケットも必要になるので作ってない場合は下の選択ボックスから作ってください。

無事作成できると、関数の「トリガー」タブから関数を起動させるためのURLを取得できます。

あとは、取得したURLを使って、Slackで自分用のスラッシュコマンドを作成します。

今回は、watanany_tilというコマンド名で作成しました(watananyは自分が使っているID、自分用という意味で)。

Slackのスラッシュコマンドの設定項目は多くないので割愛します。

ソースコード

Google Cloud Functionsでは以下のソースコードを入力しデプロイします。

index.js

'use strict';
const axios = require('axios');
const GitHubApi = require('github');
const moment = require('moment-timezone');

const config = {
  slack: {
    verifyToken: '<アドウェイズのSlackからリクエストが来たかをチェックするためのトークン(スラッシュコマンドの設定から取得できる)>',
    userName: '<Slackでのユーザー名>',
  },
  github: {
    owner: '<GitHubのID>',
    repo: '<GitHubのリポジトリ名>',
    accessToken: '<GitHubのPersonal Access Token>',
  }
};

function handlePost (req, res) {
  if (verifyRequest(req)) {
    const [fileName, content] = parseRequest(req.body.text);
    const time = moment().tz('Asia/Tokyo').format('YYYYMMDDHHmmss');
    const path = `${time}_${fileName}.md`;
    const url = `https://github.com/${config.github.owner}/${config.github.repo}/blob/master/${path}`;
    
    createFile(path, content, 'from slack').then((val) => {
      axios.post(req.body.response_url, { 
        response_type: 'in_channel',  // チャンネル参加者にも見えるように(デフォルトはOnly visible to you)
        text: url,
        attachments: [{ text: content }]
      });
    }).catch((reason) => {
      axios.post(req.body.response_url, { text: `Error: ${reason}` });
    });
    res.status(200).json({ text: `${url} will be created.` });   
  } else {
    res.status(403).end();
  }
}

function verifyRequest(req) {
  const conditions = [
    verifySlack(req.body.token),
    verifyPoster(req.body.user_name)
  ];
  return conditions.every(c => c);
}

function verifySlack(token) {
  // アドウェイズのSlackから来ているか?
  return config.slack.verifyToken === token;
}

function verifyPoster(userName) {
  // ユーザー名が自分か?
  return config.slack.userName === userName;
}

function parseRequest(text) {
  // Slackからのリクエストの解析
  // ファイル名とファイルの中身を分割している
  const lines = text.split(/\n/);
  const path = lines[0].match(/\S+/)[0];
  const content = lines.slice(1).join("\n");
  return [path, content];
}

function createFile(path, content, message) {
  const github = new GitHubApi();
  github.authenticate({ type: 'token', token: config.github.accessToken });

  return github.repos.createFile({
    owner: config.github.owner,
    repo: config.github.repo,
    path: path,
    message: message,
    content: new Buffer(content).toString('base64'),
  });
}

exports.createTilFromSlack = function (req, res) {
  switch (req.method) {
    case 'POST':
      handlePost(req, res);
      break;
    default:
      res.status(500).send({ error: 'Something blew up!' });
      break;
  }
};

package.json

{
  "name": "sample-http",
  "version": "0.0.1",
  "dependencies": {
    "github": "12.0.7",
    "axios": "0.17.1",
    "moment-timezone": "0.5.14"
  }
}

使い方

Slackで、

/watanany_til ファイル名
# 学んだこと
## 学んだこと
blah blah blah

とすれば、

f:id:AdwaysEngineerBlog:20171205121051p:plain

という感じでレスポンスが返ってきます!

感想

Slackは手軽に書き込めるのが良いところですが、散在していたものをまとめるようにしていきたいです。

今回は完全に自分用に作成しましたが、スラッシュコマンドの設定画面は同じグループの人(つまりアドウェイズの人)は誰でも見ることができるので、リクエスト先のURLも見ることができちゃいます。

セキュリティを考えると、Slashコマンドでパスワードを送るなどもう一手間必要ですね。

また、自分用で作成しましたが、Slackのスラッシュコマンドでは入力時に補完候補が出てくるかの設定もできるので他の人の邪魔にもならないと思います。

それでは!