SlackからGitHubにissueを登録するアプリをGoogle App Scriptを使って作ってみた

SlackからGitHubにissueを登録するアプリをGoogle App Scriptを使って作ってみた

昨今、タスク管理のサービスは様々ありますが、僕はGitHubにプライペートなリポジトリを作ってissueをタスク管理の仕組みとして使っています。

そんな中でいくつかのissueを連続して登録するときに「毎回ボタンをポチポチ押すのが面倒だな〜」と思うことがあったので、slackからバシバシissue登録できる仕組みを作ってみました。

GitHubのissueでタスク管理する

いままでTodoistやTrelloを試してきましたが、なかなかしっくりこなくて、あっちのサービス・こっちのサービスと渡り歩いてきました。

ある時、「なるべくGitHubのissueに登録して管理している。細かな作業はそれ用のリポジトリを作っている。」という知り合いがいて、それをヒントにGitHubにタスク管理用のプライペートリポジトリ作って、issueでタスク管理するようにし始めました。

issueの一覧はTodoリストとして確認できるし、Projectを作ればカンバン方式で案件の進捗具合を確認できます。

さらには、issueを作ったりcloseしたりするとcontributionsのグラフに色がついて活動してる感じがでます。
大事ですよね。活動してる”風”。

ただ、issueを登録するときの煩わしさと仲良くする必要がありまして…

GitHubのissue登録

issueを1件登録するために「New issue」ボタン押す→タイトル入力→「Submit new issue」ボタン押す…

というややめんどくさい手順が必要になります。
issue1件だけならいいのですが、タイミング的に複数件一気に登録したいときは面倒くささ100倍です…

そんな面倒くささを解消するために、普段開きっぱなしにしているslackからサクッとissue登録するための仕組みを作ってみました。

slackからGitHubにissueを登録する仕組み

前置きが長くなりましたが、今回作ったslackからGitHubにissueを登録する仕組みを紹介します。

構成

構成というか、使うサービス・仕組みはこちら▼

  • slack
  • Google App Script
  • GitHub GraphQL API v4

slackからスラッシュコマンドでGoogle App Scriptで作ったWebアプリを実行します。

Google App Scriptを使っているのは、自分だけが使うオレオレツールなのでWebアプリのURLがわけわからんURLでも問題ないのと、GASならサーバーとかドメインとか気にしなくていいのでお手軽だからです。

GitHubへのissue登録はREST APIではなくGraphQL APIを使ってみます。
ココらへんは単に「せっかくだから使ってみよう」と思っただけです。自分だけしか使わないツールなので動かなくなってもいいのでなるべく挑戦していきます。

GitHubへissueを登録するGoogle App Script

GitHub GraphQL API v4を使ったissueの取得・登録は以前に試した記事をご参考にしてください。

GitHub GraphQL API v4をGoogle App Scriptから実行してissue一覧を取得してみた
GitHubのGraphQL API v4をGoogle App Scriptで叩いてissueの一覧を取得してみました。 最終的にはSlackからGitHub…
yosiakatsuki.net
GitHub GraphQL API v4をGoogle App Scriptで叩いてissueを登録してみた
SlackからGitHubにissueを登録する仕組みを作りたい記事の第2弾です。 今回はGitHub GraphQL API v4を使って試しにissue登録…
yosiakatsuki.net

作ったGASのコードはこちら▼

/**
 * Config
 */
var ACCESS_TOKEN = '作成したアクセストークン';
var ENDPOINT = 'https://api.github.com/graphql';
var REPO_ID = 'issue登録するリポジトリのID';

/**
 * doGet
 */
function doGet(e) {
  return createTextOutput('Hello World!');
}

/**
 * doPost
 */
function doPost(e) {
  //受け取ったパラメーターの分解
  var param = parseParameter(e);
  if(param.title=='') {
    return createTextOutput('titleの指定は必須です。');
  }
  createGitHubIssue(param.title,param.body,param.project,param.label);
  return createTextOutput('');
}

/**
 * パラメーターの分解
 */
function parseParameter(e) {
  var param = e.parameter.text.split(' ')
  //title
  var title = param[0];
  param.shift();
  //body
  var body = '';
  if(param.length>=1) {
    //残りを全部本文にする
    body = param.join(' ');
  }
  //projectとlabelは後で分解方法考える
  var param_data = {
    'title': title,
    'body':  body,
    'project' : '',
    'label' : '',
  };

  return param_data;
}

/**
 * Issue登録
 */
function createGitHubIssue(title,body,project,label) {
  //mutation
  var mutation = createMutation(title,body,project,label);

  var options = {
    'method' : 'post',
    'contentType' : 'application/json',
    'headers' : {
      'Authorization' : 'Bearer ' +  ACCESS_TOKEN,
      'Accept' : 'application/vnd.github.starfire-preview+json',
     },
    'payload' : JSON.stringify({query:mutation})
  };
  return post(options);
}

/**
 * Issue作成mutation作成
 */
function createMutation(title,body,project,label) {
  return 'mutation {\
    createIssue('+createIssueInput(title,body,project,label)+') {\
      issue {\
        title,\
        url\
      }\
    }\
  }';
}

/**
 * Issue作成の為のinputを作成
 */
function createIssueInput(title,body,project,label) {
  var repositoryId = 'repositoryId:"'+REPO_ID+'"';
  title = ',title:"'+title+'"';
  if(body!='') {
    body = ',body:"'+body+'"';
  }
  if(project!='') {
    project = ', projectIds:['+getProjectIds(project)+']';
  }
  if(label!='') {
    label = ',labelIds:['+getLabelIds(label)+']';
  }
  return 'input:{'+repositoryId+title+body+project+label+'}';
}

/**
 * プロジェクトIDの取得
 */
function getProjectIds(project) {
  return '';
}

/**
 * ラベルIDの取得
 */
function getLabelIds(label) {
  return '';
}

/**
 * queryの実行
 */
function getGitHubData(query) {
  var options = {
    'method' : 'post',
    'contentType' : 'application/json',
    'headers' : {
      'Authorization' : 'Bearer ' +  ACCESS_TOKEN
     },
    'payload' : JSON.stringify({query:query})
  };
  return post(options);
}

/**
 * UrlFetchApp.fetch
 */
function post(options) {
  //Issue登録のときの'Accept' : 'application/vnd.github.starfire-preview+json' がなくなればもう少しさっぱりまとめられる
  var response = UrlFetchApp.fetch(ENDPOINT, options);
  var json = JSON.parse(response.getContentText());

  Logger.log(json);
  return json;
}

/**
 * 結果返却用JSON作成
 */
function createJSONOutput(data) {
  return ContentService.createTextOutput(JSON.stringify(data)).setMimeType(ContentService.MimeType.JSON);
}
/**
 * 結果返却用テキスト作成
 */
function createTextOutput(text) {
  return ContentService.createTextOutput(text);
}

Google App Scriptを作成したらウェブアプリケーションとして登録し、URLを取得しておきます。

GASのウェブアプリケーション登録

ウェブアプリケーションとして登録する手順は前々回の記事を参考にしてください▼

GitHub GraphQL API v4をGoogle App Scriptから実行してissue一覧を取得してみた
GitHubのGraphQL API v4をGoogle App Scriptで叩いてissueの一覧を取得してみました。 最終的にはSlackからGitHub…
yosiakatsuki.net

slackのスラッシュコマンドを登録

slackから実行するコマンドを以下のリンクから登録します。

Create an App をクリックしてアプリの登録を開始

「Create an App」をクリックします。

アプリ名と追加するワークスペースを選択

アプリの名前と有効化するワークスペースを選択します。

スラッシュコマンドの設定をする

続いて、「Slash Commands」メニューを選択し、「Create New Command」をクリックします。

コマンドのパラメーター設定

スラッシュコマンドの設定で以下の内容を入力します。

  • Command : slackで自分が入力することになるコマンド名
  • Request URL : GASをウェブアプリケーションとして登録したURL
  • Short Description : アプリの簡単な説明
  • User Hint : コマンドを入力したときに表示される入力のヒント
作成したAppをinstallする

設定が完了したら、「Install App」メニューを選択し、「Install App to Workspace」をクリックします。

slackからコマンドを実行してみる

実際にSlackでスラッシュコマンドを入力してみて、サジェストされればOKです!

Slackからissueを登録

slackからissueを登録する

issueの登録はスラッシュコマンドの後にタイトルと本文をスペース区切りで入力します。

1つ目はタイトルとして登録され、残りは全て本文になります。本文は一応複数行の登録に対応したつもりです。

GitHub Appを有効化しておくと、登録結果を通知できる

GitHub Appをワークスペースにインストールしてリポジトリをsubscribeしておけば、issue登録時に通知を受け取れます。

GASのreturnで組み込むこともできますが、GitHub Appに任せたほうがアイコンとかもいい感じにでるのでオススメです。

まとめ

これでサクサクとslackからissueを登録できるようになりました!

GASを使ってるので、やろうと思えば定期的にissueやマイルストーンなども作れそうなのでいろいろ自動化ができるかなーと思います。

ではまた。