LoginSignup
3
0

More than 3 years have passed since last update.

Google App ScriptでQiitaのテンプレートから記事を投稿するSlackのスラッシュコマンドをつくる

Posted at

GASでスラッシュコマンドをサクッとつくれるとTwitterで見たので自分もやってみました。
ネタBotでもよかったのですがそっちのセンスがないので真面目なやつをつくりました。

今回始めてGASに触れたのですが、サーバーレスのような実行環境とソースコード管理機能をまとめて提供しており、ちょっとしたWebアプリケーションを簡単につくることができとっても便利!:hugging:

つくったもの

Slackから、Qiita:Teamのテンプレートを指定して記事を作成してくれるというシンプルなスラッシュコマンド。
チームで振り返り記事を作成する際に使っています。

Slackからコマンドを実行 スクリーンショット 2020-02-24 17.34.32.png
しばらくすると記事が作成された通知が来る スクリーンショット 2020-02-24 17.33.38.png

全体の処理の流れ

スラッシュコマンドは3秒以内にレスポンスが帰ってこないとエラーを返す仕様ですが、今回は記事作成が3秒を超えてしまう場合があったため非同期で記事を投稿するようにしました。
なので、記事の作成が最大1分ほどかかるちょっと残念な実装となってます。:sweat_smile:

QiitaAPI.png

  1. Slackからコマンドを発行
  2. doPost()がコマンドを受ける
  3. addJobQueue()がコマンド内容をテキストでCacheServiceにキューイング
  4. キューに追加された時点で一度処理を受け付けた旨をSlackに返す
  5. トリガーが1分に1回実行され、キューがあれば取り出す(なければここで終了)
  6. CacheServiceからコマンドを取得し、指定されたテンプレートのQiita記事を作成する
  7. 作成された記事のタイトルとURLをSlackにPostする

ざっくり解説

doPost()関数

function doPost(e) {
  var verificationToken = e.parameter.token;
  if (verificationToken != PropertiesService.getScriptProperties().getProperty('SLACK_VERIFICATION_TOKEN')) {
    throw new Error('Invalid token');
  }

  var [command, id] = e.parameter.text.split(' ');
  switch(command) {
    case 'template':
      addJobQueue(command, id, e.parameter.channel_id);
      var response = { text: 'テンプレートから記事を作成します:cattyping:' };
      break;
    case 'template_list':
      result_message = templateList();
      var response = { text: result_message };
      break;
    default:
      var response = { text: 'なぞのコマンド:catsurprise:' };
      break;
  }

  return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}

POSTリクエストが来た際に呼ばれる関数。

許可されたリクエストのみ受け付けるため、tokenのチェックを行っています。
tokenは以下から取得します。
https://api.slack.com/apps

  • 対象のAppを選択=>下の方にある「Verification Token」の内容をコピー。
  • GASの「ファイル」=>「プロジェクトのプロパティ」で「SLACK_VERIFICATION_TOKEN」というプロパティを追加しコピーした値を貼り付ける
スクリーンショット 2020-02-24 16.19.50.png
スクリーンショット 2020-02-24 17.10.42.png

/qiita template 1というコマンドの場合、e.parameter.text.split(' ')とすることで["template", "1"]と配列形式で取得できます。
あとはテキストを判別して処理を実行させます。

(ソースコードのcase文になるtemplate_listコマンドは登録されているテンプレート一覧を返すもの。単純なので割愛します。)

addJobQueue()

こちらの方のソースをそのまま使わせていただきました。
(こちらの記事が今回大いに参考になりました!ありがとうございます!)

CacheServiceはテキストしか保存できないので、必要な情報を;で区切りテキスト形式でまとめています。
(保存しているものは、コマンド、ID、Slackで通知するID)

これでSlackに一旦処理を受け付けた旨を返します。

トリガー(cron)

GASのトリガーはUnixでいうところのcronのような動きをしてくれる機能で、一定間隔で関数呼び出しを行ってくれます。
今回はできるだけ早くレスポンスを返したいので呼び出し間隔を1分に設定し、timeDrivenFunction()を実行するよう指定します。

トリガーは以下から追加できます。

スクリーンショット 2020-02-24 17.44.46.png

createArticleFromTemplate()

テンプレートを取得し、記事を投稿する処理をまとめています。
それぞれ説明します。

template()

指定のテンプレートをQiita APIで取得します。

function template(template_id) {
  var url = getQiitaUrl() + "/api/v2/templates/:template_id".replace(':template_id', template_id);
  var options = {
     "method" : "get",
     "headers" : headers(),
     "muteHttpExceptions": true
  };
  var response = UrlFetchApp.fetch(url, options);
  var text = response.getContentText();

  return replaceDate(text);
}

UrlFetchAppを使ってGETでリクエストします。
今回はQiita:TeamのURLを使っていてプロパティに保存していましす。


function getQiitaUrl() {
  PropertiesService.getScriptProperties().getProperty('QIITA_URL')  
}

Qiita APIで必要なトークンはQiita:Teamの設定から「個人用アクセストークン」で取得できます。
こちらもプロパティに保存します。

function headers() {
  var token = PropertiesService.getScriptProperties().getProperty('QiITA_ACCESS_TOKEN');
  return {
    "Authorization": "Bearer " + token
  };
}

createArticle()

取得したテンプレートを分解し、記事データを作成します。
細かい仕様についてはQiita API v2 documentation - Qiita:Developerを参照してください。

function createArticle(template_json) {
  var obj = JSON.parse(template_json);
  var article = {
    "body" : obj.body,
    "coediting" : true,
    "group_url_name" : null,
    "private": false,
    "tags": obj.expanded_tags,
    "title": obj.title,
    "tweet": false
  };
  return article;
}

post()

記事の投稿を行います。

function post(article_json) {
  var url = getQiitaUrl() + "/api/v2/items";

  var options = {
    "method" : "post",
    "contentType" : "application/json",
    "payload" : JSON.stringify(article_json),
    "headers" : headers(),
    "muteHttpExceptions" : false
  };

  var response = UrlFetchApp.fetch(url, options);
  if (response.getResponseCode() >= 300) {
    return "記事の作成に失敗したよ:man-gesturing-no:\n" + response.getContentText();
  }
  var obj = JSON.parse(response.getContentText());
  return "記事を作成したよ:man-raising-hand:\n" + obj.title + "\n" + obj.url + "";
}

ヘッダーはテンプレート取得時に使用したものと同様です。
HTTP Response Statusによってメッセージを切り替えています。

postSlackMessage()

結果をSlackにPostします。

function postSlackMessage(message, channelId) {
  var token = PropertiesService.getScriptProperties().getProperty('SLACK_LEGACY_TOKEN');

  var slackApp = SlackApp.create(token);
  var options = {
    username: "Qiita Post Article"
  }

  slackApp.postMessage(channelId, message, options);
}

Slackのライブラリを使わせてもらっています。
こちらのライブラリでレガシートークンが必要だったのでこちらから取得してプロパティに登録しておきます。

ライブラリは以下のように追加します。

スクリーンショット 2020-02-24 17.25.36.png
スクリーンショット 2020-02-24 17.25.49.png

(図書館て...w)

参考

3
0
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
3
0