はじめに
本記事は下記を参考にしたものです。
背景
ちょっとした心がけですが、お昼休憩に入るときはSlackのステータスを「🍙 おひる」に手動で変更しています。
こうしている理由は、仕事の連絡やメンションをくれた方が「あれ?返事遅いな」と思った際に、ステータスを見て「休憩か」とわかってもらうためです。
でも、結構な頻度で変更し忘れる & 手動でやるのが単純にめんどくさいので、「誰か同じことをうまくやっていないかな」と思い記事を書くに至っています。
うらばなし
Google Calendar と Slack を連携させる
実は、Googleカレンダーに予め予定を入れておき、予定の時間になると、Slackのステータスが切り替わる機能を使用することは、現状ではマウスでポチポチやれば簡単にできるはずです。著者も過去に使っていました。
ステータスが「in a meeting」になるだけで、内容は不明。
お昼ごはん中なのか、会議中なのか、外出中なのかは、本人以外はわからない状況。
まぁこれはこれで良くて、どんな会議なのか知られる必要が特にない方にとってはgoodな機能。
しかし、やはり私としては「ご飯中」だ、「会議中」だ、「犬の散歩中」だ、「病院なう」だということを、slackのステータスにどうしても表示させたい!!という個人的な意向もあり、今回GASを使ってやってみるということになりました。
やりたいことのイメージ
ざっと箇条書きにするとこんな感じです。
1→2を3の方法で繰り返し続けるというイメージでしょうか(ただし4をオプションでやりたい)。
- Googleカレンダーの予定タイトルを取得する
- 取得したタイトルをSlackのステータスとして表示する
- 何分かの間隔で繰り返し取得し続け、ステータスを維持・更新・削除する
- 予定がない場合は指定したtextや絵文字を表示できるようにする
準備する
必要なものは以下の3点だと思います。
- ① GoogleカレンダーID
- ② アクセスTOKEN
- Slackアプリ作成する
- Slack API取得する
- ③ GASコード
- コード内に必要情報を埋め込む
- トリガーの設定
① GoogleカレンダーID
- Googleカレンダーにアクセスする
- Slackのステータスに使いたいカレンダーの「設定と共有」を開く
- 「カレンダーの統合」を開く
- カレンダーIDをコピーして保存しておく
- グループのカレンダーだと「@」以降のidがgroup.cakender.google.comになるようですが、気にせずコピーする。
② アクセスTOKEN
まずは、Slack APIを使うためにSlackアプリを作成します。
- Slack apiページから「create NewApp」を開く(From scratchでOK)
- 詳細ページの「OAuth & Permissions」を開き「Scope」へ
- 「AppName」を適宜記入し、適用する「Workspace」を選択 > 「Create App」へ
- 「Add an OAuth Scope」をクリックし、”users.profile:write”を追加する
- 「OAuth Tokens & Redirect URLs」で「Install to Workspace」をクリック
- 権限リクエストを許可する
- 取得した「User OAuth Token」をコピーして保存しておく
③ GASコード
GASコードはgoogleドライブから簡単に作成できます。
-
G Suite Developer Hubにアクセスし、「Start Scripting」をクリック
- もしくは、GoogleDrive画面から「マイドライブ」 > 「その他」 > 「Google App Script」 で作成
- Googleアカウントでログインし、「新しいプロジェクト」で新規作成する。
- 下記に添付しているコードを貼付け、保存する。
- 補足:21/10/15 コードをきれいにインデントしました
- 引用元:この記事 の 「GASコード」部分
ちなみに、SlackAPIの公式ドキュメントにいろんな情報がのっているので載せておきます。
特に"var event_status"のbody部分には様々なステータスがあるようです。
/*******************************************
その1:Googleカレンダー予定 → Slackステータス用に整形し返す
*******************************************/
function createStatusText(event) {
// 整形した開始時刻・終了時刻
var start = event.getStartTime().getHours() + ":" + ("00" + event.getStartTime().getMinutes()).slice(-2);
var end = event.getEndTime().getHours() + ":" + ("00" + event.getEndTime().getMinutes()).slice(-2);
// ステータステキスト
var text = event.getTitle() + " (" + start + "〜" + end + ")";
// イベントがある時のステータス
var event_status = {
"profile": JSON.stringify({
"status_text": text,
"status_emoji": ":すきなアイコンのコマンドを入力する:",
"status_expiration": 0
})
};
return event_status;
}
/*******************************************
その2:作成済ステータス → SlackWebAPI経由 → プロフィールへ
*******************************************/
function postSlackStatus(status) {
// アクセス情報
const TOKEN = "準備の②で取得したUser OAuth Tokenをここに";
const URL = "https://slack.com/api/users.profile.set";
// HTTPヘッダー
const headers = {
"Authorization" : "Bearer " + TOKEN
};
//POSTデータ
var option = {
"Content-Type": "application/json",
"headers": headers,
"method": "POST",
"payload": status
};
var fetch = UrlFetchApp.fetch(URL, option);
}
/*******************************************
その3:カレンダーの予定取得 → Slackステータスを更新
*******************************************/
function main() {
// カレンダーID
const ID = "準備の①で取得したカレンダーIDはここ";
// 今日の日付
var date = new Date();
// カレンダーから今日の予定を取得
var calendar = CalendarApp.getCalendarById(ID);
var events = calendar.getEventsForDay(date);
// 今日のイベントがない場合は何もしない
if (events.length !== 0){
// イベントがないとき
var set_status = {
"profile": JSON.stringify({
"status_text": "MTG/Talk/Huddle OK Time!",
"status_emoji": ":shigotoneko:",
"status_expiration": 0
})
};
for (var i in events){
// 終日イベントの場合はスルー
if (events[i].isAllDayEvent()) {
continue;
}
// 今が予定の開始時刻以降で終了時刻以前なら今はその予定の最中 -> ステータス変更
if (events[i].getStartTime() <= date && events[i].getEndTime() >= date) {
set_status = createStatusText(events[i]);
break;
}
}
postSlackStatus(set_status);
}
}
コード手直し
その1〜その3のブロック(関数)で成り立っているコードになっています。
各自準備してもらった内容を記入したり、表示内容を決めてもらう必要がある箇所は、以下の通りです。
- "status_emoji": ":すきなアイコンのコマンドを入力する:"
+ "status_emoji": ":calender:"
const ID = "準備の①で取得したカレンダーIDはここ";
"status_text": "Free time!",
"status_emoji": ":dancer:"
const ID = "準備の①で取得したカレンダーIDはここ";
コードを動かす
さて、コードを動かす準備は整いました。
コード実行時には、どのメソッドを最初に動かすかを指定できます。
必ず、今回はmainを選択してから実行するようにしましょう。
また、初回のみGoogleカレンダーへのアクセスの確認が必要なため、その設定も済ませてください。
うまく動けば、画面下部分の「実行ログ」に開始と終了のお知らせが表示されるはずです。
定期実行の設定をする
ここまでこれば、あとは定期実行させる設定をするのみです。
- 画面の左部分から時計のマーク「トリガー」をクリックする
- 「トリガーを追加」をクリックする
- 赤枠部分の設定を行い保存をクリックする
- 赤枠以外の部分はお好みでどうそ
こうすることで、指定した時間間隔でGASが定期実行されます。
完成すると
カレンダーで作成した予定がSlackのステータスに反映されていることが確認できるはず。
補足:アイコン七変化(21/10/15追記)
予定内容に特定の文字が含まれている場合、内容と関連するアイコン表示をさせたい!とおもい、アイコンのバライティを増やすことにしました。
数パターンの分岐を追加してみました。「その1」のコード部分だけ掲載しておきます(それ以外の部分その2と3はそのままでOK)。
コード全文はこちら
/*******************************************
その1:Googleカレンダー予定 → Slackステータス用に整形し返す
*******************************************/
function createStatusText(event) {
// 整形した開始時刻・終了時刻
var start = event.getStartTime().getHours() + ":" + ("00" + event.getStartTime().getMinutes()).slice(-2);
var end = event.getEndTime().getHours() + ":" + ("00" + event.getEndTime().getMinutes()).slice(-2);
// ステータステキスト
var text = event.getTitle() + " (" + start + "〜" + end + ")";
// イベントがある時のステータス
if(/(院|健康診断)/.test(event.getTitle())){
var event_status = {
// 病院系統のとき
"profile": JSON.stringify({
"status_text": "病院なう" + " (" + start + "〜" + end + ")",
"status_emoji": ":hospital:"
})
};
}else if(/(予定あり|外出|訪問|社外)/.test(event.getTitle())){
var event_status = {
// 社外のとき
"profile": JSON.stringify({
"status_emoji": ":gaisyutu_now:"
})
};
}else if(/(打ち合わせ|社内|MTG|mtg|定例|会議|確定|up|UP|様|on|meet)/.test(event.getTitle())){
var event_status = {
// MTG・定例・1on1・打ち合わせ系統のとき
"profile": JSON.stringify({
"status_text": "MTG中" + " (" + start + "〜" + end + ")",
"status_emoji": ":google_meet_new:"
})
};
}else if(/(休|午後|午前)/.test(event.getTitle())){
var event_status = {
// 休暇系統のとき
"profile": JSON.stringify({
"status_text": text,
"status_emoji": ":palm_tree:"
})
};
}else if(/(LUNCH|lunch|Lunch|お昼|おひる|ご飯|ごはん)/.test(event.getTitle())){
var event_status = {
// ご飯
"profile": JSON.stringify({
"status_text": "ごはん食べてます" + " (" + start + "〜" + end + ")",
"status_emoji": ":rice_ball:"
})
};
}else if(/(作業|自作業|TODO|todo|Todo|案件作業)/.test(event.getTitle())){
var event_status = {
// 作業系統
"profile": JSON.stringify({
"status_text": "作業中" + " (" + start + "〜" + end + ")",
"status_emoji": ":heads-down:"
})
};
}else if(/(dog|cat|散歩|walk)/.test(event.getTitle())){
var event_status = {
// 散歩系統
"profile": JSON.stringify({
"status_text": "散歩中" + " (" + start + "〜" + end + ")",
"status_emoji": ":walking-the-dog:"
})
};
}else if(/(退社|退勤|定時)/.test(event.getTitle())){
var event_status = {
// 退勤系統
"profile": JSON.stringify({
"status_emoji": ":taikinn:"
})
};
}else if(/(出社|移動)/.test(event.getTitle())){
var event_status = {
// 移動系統
"profile": JSON.stringify({
"status_text": "移動中" + " (" + start + "〜" + end + ")",
"status_emoji": ":train:"
})
};
}else{
var event_status = {
// それ以外の予定
"profile": JSON.stringify({
"status_text": text,
"status_emoji": ":spiral_calendar_pad:"
})
};
}
return event_status;
}
Slackではこんな感じで表示されます。