Chatwork APIキーと Google Spread Sheetで 組織内のチャットグループの管理をする

Akito Kageyama Akito Kageyama

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回目)
​​​​​​​(登場人物名、団体名等は全て架空のものです)


判断に必要な情報はゲットできてる感じですね!




現場からは以上です!最後まで読んでいただいてありがとうございました!


もしお困りごとなどありましたら、お気軽に弊社までお問い合わせくださいね。解決のお手伝いができるかもしれません!



この記事のタグ

この記事を書いたスタッフ

Akito Kageyama

企画制作部 クリエイティブデザイン課

総務部 情報システム管理課

記事一覧

昨日までなかったものを、今日つくりましょう

すべての記事一覧へ

WORKS

  • 企業PR動画
  • コーポレートサイト
  • 【THIS IS ARIDA】~そこで見た景色は~ みかん畑編(有田市)
  • 六甲山上スマートシティ構想PR動画(神戸市)
制作実績一覧へ

ハリマニックス株式会社を知る