🔗目次
🔗まえがき
皆さんどうもこんにちは、この度晴れて株式会社ECN所属インターンから正社員となりましたFuseです。
株式会社ECNでは近頃、特に納期がキツくないけど誰でもいいから手が空いたらやって欲しいという社内の仕事を書き込んで管理したり依頼したり受けたりするTODOリストが登場しました。私もちょくちょく確認しては、受けれそうな仕事を受けて提出したり、様々な提案を書き込んだりしています。
ですが現状依頼の書き込みやステータスの更新を通知する手段がなく、いちいちスプレッドシートに見に行く必要があります。新しい依頼の追加や完了報告もスプレッドシートからでしか行えません。
そこで今回はSlackに依頼が追加された、または内容が更新されたときにSlack上に通知する仕組みを作っていきます。
↑目次に戻る
🔗今回作りたいもの
- 必須のカラムが埋まって、ステータスが"下書き",""(空)以外になったとき新しいレコードの情報をSlackに通知する
- カラムの内容が更新されたとき、ID、タイトル、変更されたカラム、変更前後の値を通知する
↑目次に戻る
🔗完成品
🔗環境構築
まずはSlack用アプリを作るための環境構築を行います。
公式のクイックスタートを参考にしながら構築していきましょう。
ステップ1 SlackCLIのインストール
まずは重要なツールであるSlackCLIをインストールします。
私はWindowsを使用しているので
irm https://downloads.slack-edge.com/slack-cli/install-windows.ps1 -outfile 'install-windows.ps1'
を実行します。macやLinuxをご使用の場合は
curl -fsSL https://downloads.slack-edge.com/slack-cli/install.sh | bash
を実行してください。
これでSlackCLIとついでに次世代プラットフォームのランタイム環境であるDenoなどの必要なあれこれが一緒にインストールされます。
管理者権限を与えないとうんともすんとも言わないので気をつけましょう。(1敗)
ステップ2 SlackCLIを承認しよう
SlackCLIのインストールが終わったらワークスペースで Slack CLI を認証してあげる必要があります。
コンソールにslack login
と入力すると認証コードが
/slackauthticket "認証コード"
の形で排出されます。それを適当なチャンネルに投稿し、表示された指示に従うと8文字の英数字が表示されるのでそれをコンソールに入力してやれば認証は完了です。
成功すると
You've successfully authenticated! >
Authorization data was saved to C:\Users\"ユーザー名".slack\credentials.json
Get started by creating a new app with slack.exe create my-app
Explore the details of available commands with slack.exe help
と表示され認証成功です。
VSCode+PowerShellを使ってる方はパスが通ってないことがあるので
[Environment]::SetEnvironmentVariable("Path", $ENV:Path + "C:\Users\1219m\AppData\Local\slack-cli\bin", [EnvironmentVariableTarget]::Machine)
で通しましょう。
↑目次に戻る
🔗Slack Appを完成させる
早速SlackAppを作っていきます。まずはチャンネルIDやスプシのURLを格納する.envファイルを作ります。
.env
CHANNEL_ID="通知先のチャンネルのID"
SPREADSHEET_URL="スプレッドシートのURL"
.envを用意したらまずはトリガーを叩いたときに実行されるワークフローを作りましょう。
workflows\QuestCreatedWorkFrow.ts
import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";
import { load } from "https://deno.land/std@0.203.0/dotenv/mod.ts";
await load({ export: true });
const SHEETURL = Deno.env.get("SPREADSHEET_URL");
const CHANNEL_ID = Deno.env.get("CHANNEL_ID");
export const QuestCreatedWorkFrow = DefineWorkflow({
callback_id: "quest_created",
title: "quest_created",
description: "Send a notice to channel",
input_parameters: {
properties: {
id: {
type: Schema.types.string,
},
target: {
type: Schema.types.string,
},
title: {
type: Schema.types.string,
},
bunya: {
type: Schema.types.string,
},
teishutu: {
type: Schema.types.string,
},
owner: {
type: Schema.types.string,
},
limit: {
type: Schema.types.string,
},
},
required: ["title", "owner", "teishutu", "bunya", "target", "id"],
},
});
const mes = `皆さん、新しい依頼が追加されましたよ!\n` +
"```\n" +
`*行番号*\n` +
`${QuestCreatedWorkFrow.inputs.id}\n` +
`*対象*\n` +
`${QuestCreatedWorkFrow.inputs.target}\n` +
`*タイトル*\n` +
`${QuestCreatedWorkFrow.inputs.title}\n` +
`*分野*\n` +
`${QuestCreatedWorkFrow.inputs.bunya}\n` +
`*担当者*\n` +
`${QuestCreatedWorkFrow.inputs.owner}\n` +
`*提出方法*\n` +
`${QuestCreatedWorkFrow.inputs.teishutu}\n` +
`*期限*\n` +
`${QuestCreatedWorkFrow.inputs.limit}\n` +
"```\n" +
`*<${SHEETURL}|早速見に行きませんか?>*`;
QuestCreatedWorkFrow.addStep(Schema.slack.functions.SendMessage, {
channel_id: CHANNEL_ID ?? "",
message: mes,
});
ワークフロー作成の流れは以下のようになっています。
1. 定義したDefineWorkflow関数でワークフローの名前や説明、入力などを定義
2. ワークフローのaddStepメソッドでワークフローに手順を追加
送信先のチャンネルのIDや投稿に埋め込むスプシのURLは.env
から取得しています。
workflows\QuestCreatedWorkFrow.ts
import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";
import { load } from "https://deno.land/std@0.203.0/dotenv/mod.ts";
await load({ export: true });
const SHEETURL = Deno.env.get("SPREADSHEET_URL");
const CHANNEL_ID = Deno.env.get("CHANNEL_ID");
export const QuestUpdatedWorkFrow = DefineWorkflow({
callback_id: "quest_updated",
title: "quest_updated",
description: "Send a notice to channel",
input_parameters: {
properties: {
id: {
type: Schema.types.string,
},
target: {
type: Schema.types.string,
},
title: {
type: Schema.types.string,
},
column: {
type: Schema.types.string,
},
oldval: {
type: Schema.types.string,
},
newval: {
type: Schema.types.string,
},
},
required: ["title", "column", "oldval", "newval", "target", "id"],
},
});
const mes = `皆さん、依頼の内容が更新されました!\n` +
"```\n" +
`*行番号*\n` +
`${QuestUpdatedWorkFrow.inputs.id}\n` +
`*対象*\n` +
`${QuestUpdatedWorkFrow.inputs.target}\n` +
`*タイトル*\n` +
`${QuestUpdatedWorkFrow.inputs.title}\n` +
`*更新されたカラム*\n` +
`${QuestUpdatedWorkFrow.inputs.column}\n` +
`*更新内容*\n` +
`"${QuestUpdatedWorkFrow.inputs.oldval}"→"${QuestUpdatedWorkFrow.inputs.newval}"\n` +
"```\n" +
`*<${SHEETURL}|早速見に行きませんか?>*`;
QuestUpdatedWorkFrow.addStep(Schema.slack.functions.SendMessage, {
channel_id: CHANNEL_ID,
message: mes,
});
クエスト更新時のワークフローも同様に作っていきます。
内容はほとんど同じですが入力内容とメッセージ生成用のテンプレートの内容が違います。
ワークフローを作ったら忘れずにmanifest.ts
に登録しておきましょう。
manifest.ts
import { Manifest } from "deno-slack-sdk/mod.ts";
import { QuestCreatedWorkFrow } from "./workflows/QuestCreatedWorkFrow.ts";
import { QuestUpdatedWorkFrow } from "./workflows/QuestUpdatedWorkFrow.ts";
/**
* The app manifest contains the app's configuration. This
* file defines attributes like app name and description.
* https://api.slack.com/future/manifest
*/
export default Manifest({
name: "ECN-Quest-Board",
description: "手が空いた時用のTODOリストの追加や更新を通知します。",
icon: "assets/Icon.png",
functions: [],
workflows: [QuestCreatedWorkFrow, QuestUpdatedWorkFrow],//ここにexportしたワークフローを追加する
outgoingDomains: [],
botScopes: ["commands", "chat:write", "chat:write.public"],
});
その後はトリガーを作っていきます。今回使うのはWebHookトリガー、外部からのWebリクエストにより発火します。
triggers\QuestCreated.ts
import { Trigger } from "deno-slack-api/types.ts";
import { QuestCreatedWorkFrow } from "../workflows/QuestCreatedWorkFrow.ts";
import { TriggerTypes } from "deno-slack-api/mod.ts";
const trigger: Trigger<typeof QuestCreatedWorkFrow.definition> = {
type: TriggerTypes.Webhook,
name: "Created Quest Notice to Channel",
description: "runs the workflow",
workflow: "#/workflows/quest_created",
inputs: {
id: {
value: "{{data.id}}",
},
target: {
value: "{{data.target}}",
},
title: {
value: "{{data.title}}",
},
bunya: {
value: "{{data.bunya}}",
},
teishutu: {
value: "{{data.teishutu}}",
},
owner: {
value: "{{data.owner}}",
},
limit: {
value: "{{data.limit}}",
},
},
};
export default trigger;
トリガーを作る際は
- トリガーの種別
- 名前
- 説明文
- 実行するワークフロー
- 入力の定義
トリガーの種別については聞きなれないと思われるので解説します。
トリガーの種別について
トリガーの種類は4種類あります。
名前 | TriggerTypes | 解説 |
---|---|---|
ショートカット | TriggerTypes.Shortcut | スラッシュコマンドやボタンで発火 |
イベント | TriggerTypes.Event | Slack内でのイベントで発火 |
スケジュール | TriggerTypes.Scheduled | 一定時間ごとに発火 |
Webhook | TriggerTypes.Webhook | 外部からのWebリクエストにより発火 |
今回はWebhookトリガーを使いたいのでTriggerTypes.Webhook
を指定します。
その後はクエスト更新時のトリガーも同様に作っていきましょう。
triggers\QuestUpdated.ts
import { Trigger } from "deno-slack-api/types.ts";
import { QuestUpdatedWorkFrow } from "../workflows/QuestUpdatedWorkFrow.ts";
import { TriggerTypes } from "deno-slack-api/mod.ts";
const trigger: Trigger<typeof QuestUpdatedWorkFrow.definition> = {
type: TriggerTypes.Webhook,
name: "Updated Quest Notice to Channel",
description: "runs the workflow",
workflow: "#/workflows/quest_updated",
inputs: {
id: {
value: "{{data.id}}",
},
target: {
value: "{{data.target}}",
},
title: {
value: "{{data.title}}",
},
column: {
value: "{{data.column}}",
},
oldval: {
value: "{{data.oldval}}",
},
newval: {
value: "{{data.newval}}",
},
},
};
export default trigger;
🔗GAS側を完成させる
WebHookを受け取る側が完成したので次はWebHookにリクエストを投げる側のプログラムを作っていきます。
あらかじめ作られていたスプレッドシートを開き、
拡張機能>Apps Scriptでスプレッドシートに紐づいたGASプロジェクトを新規作成できます。
あとはこの記事で紹介した方法でスプレッドシートの更新を検知できるようにしましょう。
出来上がったコードはこちらになります。
function onEditCell(e) { // セルが編集されたら実行
console.log(e.source.getSheetName())
if(e.source.getSheetName()!="やって欲しいことリスト") return; // 編集されたシートが一時作業用シートでなければキャンセル
const ActiveSheet=e.source.getSheetByName("やって欲しいことリスト"); // 一時作業用シートをオブジェクトとして取得
console.log(e.source.getSheetName())
if(e.range.getRow()<=2)return//見出し行以上ならキャンセル
const range = ActiveSheet.getRange(e.range.getRow(),1,1,12); // 編集された行を取得
const data=range.getValues()[0]//データ取得
console.log(data)
if(data[1]=="") return; // 「対象」は入力されているか?
if(data[2]=="") return; // 「内容」は入力されているか?
if(data[3]=="") return; // 「分野」は入力されているか?
if(data[5]=="") return; // 「担当者」は入力されているか?
if(data[7]=="") return; // 「状態」は入力されているか?
if(data[7]=="下書き"||data[7]=="キャンセル") return; // 「状態」は下書きやキャンセルでないか?
if(data[8]=="") return; // 「提出方法」は入力されているか?
if(data[10]=="") return; // 「期日」は入力されているか?
if(data[11]==""){//まだ通知されてないなら
NoticeCreatedQuest(data)
const targetcell = ActiveSheet.getRange(range.getRow(),12); // 編集された行を取得
targetcell.setValue("済")
}else{
const columnname=ActiveSheet.getRange(2,e.range.getColumn()).getValues()[0][0]
NoticeUpdatedQuest(data,columnname,e.oldValue,e.range.getValues()[0][0])
}
}
function NoticeCreatedQuest(data){
const url = PropertiesService.getScriptProperties().getProperty('CREATED_WEBHOOK') // トークンを取得
var headers = { // HTTPリクエストのヘッダー
"Accept": "application/json",
"Content-type": "application/json"
}
var data = { // データ部
"id":data[0].toString(),
"target":data[1],
"title":data[2],
"bunya":data[3],
"teishutu":data[8],
"owner":data[5],
"limit":data[10]
}
var options = { // リクエストのオプション(メソッド・データ・ヘッダー)
"method": "post",
"payload": JSON.stringify(data),
"headers": headers
};
var resp=UrlFetchApp.fetch(url, options);//リクエストを送信
}
function NoticeUpdatedQuest(data,column_name,oldval,newval){
const url = PropertiesService.getScriptProperties().getProperty('UPDATED_WEBHOOK') // トークンを取得
var headers = { // HTTPリクエストのヘッダー
"Accept": "application/json",
"Content-type": "application/json"
}
var data = { // データ部
"id":data[0].toString(),
"target":data[1],
"title":data[2],
"column":column_name,
"oldval":oldval,
"newval":newval
}
var options = { // リクエストのオプション(メソッド・データ・ヘッダー)
"method": "post",
"payload": JSON.stringify(data),
"headers": headers
};
var resp=UrlFetchApp.fetch(url, options);//リクエストを送信
}
12列目の通知済みフラグの値の有無に応じて異なるエンドポイントにデータを送信しています。また、WebHookのURLはハードコーディングするのではなくGASのスクリプトプロパティという機能を使って格納、取得しています。
↑目次に戻る
🔗 Slackアプリをデプロイする
完成したslackアプリは
slack deploy
でデプロイできます。
アプリデプロイ後はslack trigger create
の選択肢にDeployed
が増えます。
slack env add
で環境変数を本番環境に設定して、
slack trigger create
で2つのトリガーをデプロイしてやれば完成です。
GASのスクリプトプロパティも忘れずに本番環境のURLに変えておきましょう。
デプロイしたことでアイコンもmanifest.ts
に設定したものに変更されます。
これにて完成です。お疲れさまでした!
🔗あとがき
これにてこの記事はおわりです。
次回はクエストの一覧の確認と受注、新規登録をSlackから行えるようにしていきます。
↑目次に戻る
株式会社ECNはPHP、JavaScriptを中心にお客様のご要望に合わせたwebサービス、システム開発を承っております。
ビジネスの最初から最後までサポートを行い
お客様のイメージに合わせたWebサービス、システム開発、デザインを行います。