Chatwork APIキーと Google Spread Sheetで 組織内のチャットグループの管理をする
2023.07.28
こんにちは!
チーフエンジニア 'K'(ケイ) です。
みなさん、ビジネスチャットツール使ってますかー?
Slackやチャットワークをはじめ、「ビジネスチャットツール」でググるとたくさんの広告や比較記事が出てきます。
弊社では「Chatwork」(チャットワーク)のお世話になっています。
チャットワークは弊社の文化やリテラシー状態に非常にマッチしていたようで、かなりスムーズに導入・浸透し、今では社内の重要なコミュニケーションツールとなっています。誰でも手軽にグループが作れるので、メンバーごと、プロジェクトごと、話題ごとにグループがつくられ、活発なコミュニケーションの場となっています。
ですが、”その時” はいつも突然訪れます。
いや、来るべき時がついに来たというべきか・・・。
グ ル ー プ 乱 立
そう、グループ乱立問題です。
これはもうほんと、どこでも「あるある」だと思います。
どうですか?
乱立してる状態はわかってるのに同じメンバーや話題のグループを探すのが面倒すぎてまた新しくグループを作ったりしていませんか?そうやっていつも問題を見て見ぬ振りをして、さらに状況を困難にしていませんか…?
あ、お腹痛くなってきた…。
ハイ…(☍﹏⁰)。
申し訳ございません……(☍﹏⁰)。
そしてついに先日、勇者が声を上げました!
「グループどないなっとんねん!?」
(ブログのためセリフは脚色しています)
グループ状況の把握
さて、チャットワークのグループは、メンバーからは見えるけど、メンバー以外からはその存在すら認識できません。なので、現状把握するだけでも関係者全員に確認しながらとなるため、リストアップするだけでも大変です。
グループ名コピーして〜、
メンバーを書き出して〜、
管理権限者をピックアップして〜、
スプレッドシートでまとめて〜・・・
仮に対象者を絞ったとしても、がっつりリソース割かれるのは目に見えています。
(だからこそ声を上げた人は勇者だと思うのです。)
さて、いよいよ僕にもその番が回ってきて、リンクから開いたスプレッドシートに追記しようとするのですが、、、
いやこれめんどいな!
いやほんと、めちゃくちゃ面倒臭い!
僕はラクをするために毎日必死で働いているので、特に面倒な作業からは光の速さで遠ざかる習性があります。(なので実際はそれほど面倒でもないのかもしれないのですが。。)いつでもどこでもまず自動化を目論みます。
ここでも息をするように自動化の手段を探し、遂に一番ラクな方法に辿り着きました。
それがこの『組織メンバー全員にAPIキーを取得してもらってGASで集計する』というものです。
まあ大袈裟なわりに単純な話なのですがw
仕様はいたってシンプルです。
工程1
①APIキー&名前のシートと集計シートを用意する
②(2回目以降)人ごとのグループ情報シートを削除する
③APIキーを全件ブン回して、全員のグループ情報シートを作成する
④APIキーを使ってグループ情報を取得・記録する
工程2
⑤ ④の結果を評価して、結果シートに グループ名、メンバー、管理者 とその他情報を記録する
以上!
処理にかかる時間は工程1で一人当たり3〜5秒、工程2で10人の状態で20秒ぐらいだったので、弊社規模(50人前後)であれば問題なく運用できそうです。
出力結果がこちら!
(登場人物名、団体名等は全て架空のものです)
あっはぁ!!超ラクぅ〜!
スプレッドシートのスクショとGASのコードを載せてみます。
※コードの細かい例外処理などは省いていますが、APIキーのシート内容だけ整えてもらえたらひとまずコピペで動くと思います。
①APIキーと名前のシートを用意する
(登場人物名、団体名等は全て架空のものです)
②(2回目以降)人ごとのグループ情報シートを削除する
③APIキーを全件ブン回して、全員のグループ情報シートを作成する
④APIキーを使ってグループ情報を取得・記録する
function getCahtworkGroups(){
// トークン一覧を取得
const tokens = getTokens();
if( Object.keys(tokens).length == 0 ){
console.log('トークンが登録されていません');
return false;
}
let ss = SpreadsheetApp.getActiveSpreadsheet();
const sheetLength = ss.getNumSheets();
// シート3以降を削除
for(var i = sheetLength - 1; i >= 2; i--){
const mySheet = ss.getSheets()[i];
const mySheetName = mySheet.getName();
console.log(mySheetName + 'を削除しました');
ss.deleteSheet( ss.getSheetByName(mySheetName) );
}
// APIキーの数だけ実行
Object.keys(tokens).forEach( (userName, i) => {
const token = tokens[userName];
// シートを追加
let newSheet = ss.insertSheet();
ss.moveActiveSheet(2 + 1 + i);
// シート名をユーザー名に変更
newSheet.setName(userName);
console.log('シート:' + userName + 'を追加しました');
// タイトル行を入力
newSheet.getRange(1,1).setValue('room_id');
newSheet.getRange(1,2).setValue('name');
newSheet.getRange(1,3).setValue('type');
newSheet.getRange(1,4).setValue('role');
newSheet.getRange(1,5).setValue('message_num');
newSheet.getRange(1,6).setValue('file_num');
newSheet.getRange(1,7).setValue('icon_path');
newSheet.getRange(1,8).setValue('last_update_time');
// グループ情報を取得して入力
getCahtworkGroup(token, newSheet);
});
}
// トークン一覧を取得
function getTokens() {
// スプレットシート読み込み
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheetTokens = ss.getSheetByName('tokens');
let data = {};
const sheetRow = sheetTokens.getLastRow() + 1;
for( let i = 2; i <= sheetRow; i++){
const vals = sheetTokens.getRange(i,2,i,4).getValues();
if(vals[0][1] && vals[0][3] == ''){
data[vals[0][0]] = vals[0][1];
}
}
return data;
}
// グループチャット一覧取得
function getCahtworkGroup(token, newSheet){
const options = {
headers : {'X-ChatWorkToken' : token},
method : 'get',
};
const url = 'https://api.chatwork.com/v2/rooms';
const respons = UrlFetchApp.fetch(url, options);
const json = JSON.parse(respons);
// スプレットシート出力
let sheetRow = newSheet.getLastRow() + 1;
for(let j = 0; j < json.length; j++){
if( json[j]['type'] == 'group'){
newSheet.getRange(sheetRow,1).setValue(json[j]['room_id']);
newSheet.getRange(sheetRow,2).setValue(json[j]['name']);
newSheet.getRange(sheetRow,3).setValue(json[j]['type']);
newSheet.getRange(sheetRow,4).setValue(json[j]['role']);
newSheet.getRange(sheetRow,5).setValue(json[j]['message_num']);
newSheet.getRange(sheetRow,6).setValue(json[j]['file_num']);
newSheet.getRange(sheetRow,7).setValue(json[j]['icon_path']);
newSheet.getRange(sheetRow,8).setValue(json[j]['last_update_time']);
sheetRow++;
}
}
}
⑤ ④の結果を評価して、結果シートに グループ名、メンバー、管理者 とその他情報を記録する
function getResults() {
// スプレットシート読み込み
let ss = SpreadsheetApp.getActiveSpreadsheet();
const sheetLength = ss.getNumSheets();
let groups = {};
for(var i = 2; i < sheetLength; i++){
let mySheet = ss.getSheets()[i];
const userName = mySheet.getName();
const myGroups = getMyGroups(mySheet);
let myGroup;
Object.keys(myGroups).forEach( (gId, index) => {
myGroup = myGroups[gId];
if(gId && groups[gId] == undefined){
groups[gId] = myGroup;
groups[gId].admins = [];
groups[gId].members = [];
groups[gId].memberNum = 0;
}
if(myGroup.gRole == 'admin'){
groups[gId].admins.push(userName);
}else if(myGroup.gRole == 'member'){
groups[gId].members.push(userName);
}
groups[gId].memberNum ++;
});
console.log('完了:' + userName);
}
console.log( Object.keys(groups).length );
// スプレットシート出力
const sheetResults = ss.getSheetByName('results');
sheetResults.getRange(2,1,1000,8).clearContent(); // 既存データクリア
let row = 1;
let data;
Object.keys(groups).forEach( (gId, index) => {
row ++;
data = groups[gId];
sheetResults.getRange(row,1).setValue( '=IMAGE("' + data.gIconPath + '")' );
sheetResults.getRange(row,2).setValue( data.gName );
sheetResults.getRange(row,3).setValue( data.memberNum );
sheetResults.getRange(row,4).setValue( data.admins.join('、') );
sheetResults.getRange(row,5).setValue( data.members.join('、') );
sheetResults.getRange(row,6).setValue( data.gMessageNum );
sheetResults.getRange(row,7).setValue( data.gFileNum );
sheetResults.getRange(row,8).setValue( timestamp2Datetime(data.gLastUpdateTime) );
});
}
function timestamp2Datetime(timestamp){
let dateAt = new Date();
dateAt.setTime(timestamp * 1000);
const year = dateAt.getFullYear();
const month = dateAt.getMonth() + 1;
const day = dateAt.getDate();
const hour = dateAt.getHours();
const minutes = dateAt.getMinutes();
const seconds = dateAt.getSeconds();
return year + '/' + month + '/' + day + ' ' + hour + ':' + minutes + ':' + seconds;
}
function getMyGroups(sheet) {
let data = {};
const sheetRow = sheet.getLastRow() + 1;
for( let i = 2; i <= sheetRow; i++){
const vals = sheet.getRange(i,1,i,8).getValues();
const aData = {
gId: vals[0][0],
gName: vals[0][1],
gType: vals[0][2],
gRole: vals[0][3],
gMessageNum: vals[0][4],
gFileNum: vals[0][5],
gIconPath: vals[0][6],
gLastUpdateTime: vals[0][7],
};
if(aData.gId){
data[aData.gId] = aData;
}
}
return data;
}
再度、実行結果はこんな感じです。(2回目)
(登場人物名、団体名等は全て架空のものです)
判断に必要な情報はゲットできてる感じですね!
現場からは以上です!最後まで読んでいただいてありがとうございました!
もしお困りごとなどありましたら、お気軽に弊社までお問い合わせくださいね。解決のお手伝いができるかもしれません!