初めての投稿につき、やさしい心で見て頂けると嬉しいです。
自分のメモ用を再編集したもので、不備が多いかと思いますが、誰かの役に立てば幸甚です。
細かい部分は覚えていない 初期設定など情報が多々ある部分は、所々省略しています。
やりたいこと
- Githubのissueを横断的にSlackで見たい
- ID、タイトル、author、URL、reactionの数(投票などをするため)
- せっかくなので、RESTAPIではなくGraphQLを使ってみたい
方法1でやったことと問題点
- 方法1では、heroku+botkitを使いやりたいことを実現しました。
- ただし、herokuの無料枠をこれだけで占有するのはもったいないので、AzureFunctionsに持っていきたいと考えて改良しました。
- 複数のSlackにまたがらせることもできませんしね
イメージ
動作環境
- MacOS 10.13.5
- git-graphql-client 1.0.0
- AzureFunctions 無料プラン
- Slack 無料プラン
- Github 無料プラン
STEP1 Azureにアカウントを作る
- Microsoft Azureはクラウドサービスの集合体です。
- 今回は、サーバーレスでファイルをあげるだけでURLを呼ぶとプログラムが実行されるAzureFuctionsを利用します。
- 類似するサービスに AmazonWebService Lambda があります。
- 今回は、無料枠 で行います。
- アカウントの作成や資材のアップロードは様々な先人の方がノウハウを残しているので省略します。
- 手抜きでごめんなさい。公式ドキュメントを参考にするとよいと思います。
- https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-create-first-azure-function
STEP2 SlackからWebHookのURLを設定する
- 日本語化している場合は、slackをカスタマイズ ⇒ App管理 ⇒ カスタムインテグレーション ⇒ 発信/着信Webフックからいけます
- 発信WebフックがAzureFunctionsを呼び出すもの
- 引き金となる言葉とazureの関数のURLを設定します。
- azure Functions側のソースコードが表示されている画面で、
関数のURLの取得
で表示されるものを設定
- 着信WebフックがAzureFUnctionsから投稿されるもの
- どこのチャンネルに投稿されるのかを指定します
- WebhookURLは、azureFunction側の送信先のURLに指定します(詳しくは後述するソースを参照)
STEP3 GithubのPersonalAccessTokenを払いだす
- 細かい手順は、こちらも様々な先人の方がノウハウを残しているので省略します。
- 手抜きでごめんなさい。先人方の記事を参考にされるとよいと思います。
- https://qiita.com/kz800/items/497ec70bff3e555dacd0
- 公開しているプロジェクトであれば、なにも権限がなくて大丈夫です
STEP4 GraphQLのクエリを作成する
- 公式APIドキュメント:https://developer.github.com/v4/
- 動作を確認するエクスプローラ:https://developer.github.com/v4/explorer/
作成したリクエスト
- owner,name,labelsは自分の条件に合わせて使ってください
- 今回は、ステータスがOpenで特定のラベルが付いたissueを集めました
- 条件の追加をする場合は、()内を公式APIドキュメントをみてカスタマイズしてください
{
repository(owner: "xxx", name: "yyy") {
issues(last: 100, states: OPEN, labels: "zzz") {
edges {
node {
number
title
url
author {
login
}
reactions(last: 100) {
nodes {
content
}
}
}
}
}
}
}
応答イメージ
{
"data": {
"repository": {
"issues": {
"edges": [
{
"node": {
"number": 1,
"title": "タイトル",
"url": "https://github.com/XXX/YYY/issues/1",
"author": {
"login": "user"
},
"reactions": {
"nodes": [
{
"content": "THUMBS_UP"
}
]
}
}
}
]
}
}
}
}
STEP5 package.jsonを書く
- 最低限の名前とバージョン、依存関係のgithub-graphql-clientを記述したpackage.jsonをファイルのアップロードでサーバに置く
- 下記のサンプルを参照
- あるいは、プラットフォームの構成でコンソールからnpmでインストールしてもOKです
{
"name": "topic-bot",
"version": "1.0.0" ,
"dependencies": {
"github-graphql-client": "1.0.0"
}
}
- girhub-graphql-client
STEP6 プログラム本体を記述する
- 関数の+ボタンで新規作成⇒HTTPtriggerで新しい関数を作る
- 許可されているHTTPメソッド:All methods
- モード:Standard
- 要求パラメータ名:req
- ルートテンプレート:ルートテンプレート
- 承認レベル:Function
- アプリケーションの設定で、Githubのトークンを設定する
プログラム本体
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
// 準備
if (!process.env["GITHUB_TOKEN"]) {
context.res = {
status: 400,
body: "please set API token environment value."
};
context.done();
}
var request = require('request');
var myquery =
`{
repository(owner: "xxx", name: "yyy") {
issues(last: 100, states: OPEN, labels: "zzz") {
edges {
node {
number
title
url
author {
login
}
reactions(last: 100) {
nodes {
content
}
}
}
}
}
}
}`;
var githubToken = process.env["GITHUB_TOKEN"];
var issues = [];
var client = require('github-graphql-client');
// githubに問い合わせ
client({token: githubToken,query: myquery}, function (err, res) {
if (err) {
context.log("error");
context.log(err);
} else {
// 成功した場合
var resData = res.data.repository.issues.edges;
for (i = 0; i < resData.length; i++) {
var issue = {};
issue.number = resData[i].node.number;
issue.title = resData[i].node.title;
issue.url = resData[i].node.url;
issue.author = resData[i].node.author.login;
issue.reaction = resData[i].node.reactions.nodes.length;
issues.push(issue);
}
function compareReaction(a,b){
return b.reaction - a.reaction;
}
issues = issues.sort(compareReaction);
// チャットに返信
var result = ""
for (i = 0; i < issues.length; i++) {
var resultLine = "#" + issues[i].number + ":" + issues[i].title + " :studio_microphone: " + issues[i].author + " :+1: " + issues[i].reaction + "pt " +
"<" + issues[i].url + "|詳細>\n\n";
result += resultLine;
}
var options = {
url: {着信Webフックで表示されているURL} ,
form: 'payload={"text": " '+ result +'"}',
json :true
};
request.post(options, function(error, response, body){
if (!error && response.statusCode == 200) {
context.log('ok');
} else {
context.log('error');
}
});
}
})
context.done();
};
- reactionsはtotalCountを使ってもOK
- 今回は、個別の合計や特定の要素は不要で、全体数がほしいのでこのようにしています。
上手くいかなかったこと
- botkitを使っても最初はうまくいくのですが、15-30分たつと接続が切れて、再接続のオプションを設定してもダメでした
- 内容にバグやセッションの維持?があると、正しく切り替えが行われないのか、多重に応答するようになりました。
STEP7 AzureFunctionsにあげて動作確認
- Slackで実際に呼ぶ