[Slack × GAS] conversations.rename チャンネル名称を変更する
なんやかんやで名称を一気に変更したい時、ありますよね。
力isパワーでポチポチ手入力で変更しても良いのですが、件数が多くてだるすぎるのでGASでどうにかしたいと考えて試行錯誤しました。
その俺メモ。
まとめると、チャンネルIDを取得して、名称変更したいIDに対してuserOAuthTokenでRenameを効かせる、ってことになる。
ちゃんとリファレンスを読むと、この辺も実は書いてあったんだな〜。ちゃんと読むの大事っすね、はい。
コード
こんな構成。joinとLeaveは結局要らんかった。いつか使うかもしれない、勉強にはなった。
global
// スプレッドシート取得.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("チャンネルIDの取得");
const targetSheet= ss.getSheetByName("名称変更対象");
// SlackAPIで登録したボットのトークンを設定する.
//直接指定の場合は = "xoxb-から始まる上記Slack側の設定で取得したトークン";
const botUserOAuthToken = PropertiesService.getScriptProperties().getProperty('BotUserOAuthToken');
const userOAuthToken = PropertiesService.getScriptProperties().getProperty('UserOAuthToken');
トークンはプロパティに入れてます。
channel_List
参照資料から改変した主なポイント
再代入しないものはconst宣言。
値を書き込む時にsetValueを都度行っていると処理時間が掛かるので、配列に格納して一気に書き込む。
削除時にsetValueを都度行っていると処理時間が掛かるので、clearメソッドを使う。と思ったけどシートは新規作成としたけど、この辺はお好みだな。
// 参照した資料
// https://shiimanblog.com/engineering/public-channels/#toc9
/**
* Main Execution method.
* 実行メソッド.
*/
function setChannelInfo() {
public_channels = getChannelList("public_channel");
const public_channels_array = public_channels.map(channel => [channel.id, channel.name, channel.topic.value, channel.purpose.value]);
const headers = ["channel.id", "channel.name", "channel.topic.value", "channel.purpose.value"];
public_channels_array.unshift(headers)
const today = Utilities.formatDate(new Date(), 'JST', 'YYYYMMdd_HH:mm:ss');
const newSheet = ss.insertSheet();
newSheet.setName("channelList_"+today);
newSheet.getRange(1, 1, public_channels_array.length, public_channels_array[0].length).setValues(public_channels_array);
}
/**
* Function to get channel list via API.
* チャンネルリスト ページャー対応.
*
* @param {string} types - Optional arguments of Slack API.
* @return {object} channelList - Slack channel list
*
* NOTE: Need to specify cursor to get more than 1000 pieces of information.
* NOTE: https://api.slack.com/methods/conversations.list
*/
function getChannelList(types) {
const maxPager = 10; // ページャーの最大数.
let count = 1;
let channelList = [];
let cursor = "";
while (count <= maxPager) {
channelObj = getChannelObject(types, cursor);
channelList = channelList.concat(channelObj.channels);
if (channelObj.response_metadata.next_cursor == "") {
break;
}
cursor = channelObj.response_metadata.next_cursor;
// console.log(cursor);
// console.log(typeof cursor);
count++;
}
return channelList;
}
/**
* Function to get channel list via API.
* APIでチャンネルリストを取得する。
*
* @param {string} types - Optional arguments of Slack API.
* @param {string} cursor - Optional arguments of Slack API.
* @return {object} obj - Slack channel list
*
* NOTE: Need to specify cursor to get more than 1000 pieces of information.
* NOTE: https://api.slack.com/methods/conversations.list
*/
function getChannelObject(types, cursor) {
const options = {
"method": "get",
"contentType": "application/x-www-form-urlencoded",
"payload": {
"token": botUserOAuthToken,
"limit": 1000,
"exclude_archived": true,
"types": types,
"cursor": cursor
}
}
const url = 'https://slack.com/api/conversations.list';
const response = UrlFetchApp.fetch(url, options);
const obj = JSON.parse(response);
return obj;
}
channel_Rename
function setRenameChannels() {
const lastRow = targetSheet.getLastRow();
const values = targetSheet.getRange(1, 1, lastRow, 2).getValues();
values.forEach(value => renameChannel(value[0], value[1]));
}
function renameChannel(channelID, channelName) {
const options = {
"method": "post",
"contentType": "application/x-www-form-urlencoded",
"payload": {
"token": userOAuthToken,
"channel": channelID,
"name": channelName
}
}
const url = 'https://slack.com/api/conversations.rename';
UrlFetchApp.fetch(url, options);
}
前提環境
ワークスペースの管理者またはオーナー権限を持っていること。
Slack Appを作成し、APIに必要なScopesを付与していること。
やりたいこと
n件の対象チャンネルのチャンネル名称を一気に変更したい
考えていた手順
名称変更したいチャンネルのIDを取得する
名称変更したいチャンネルにBotを参加させる →名称変更は管理者権限で行うからいらんかった
名称変更を行う
使用するAPI
conversations.list
https://api.slack.com/methods/conversations.list
conversations.join →要らんかった
https://api.slack.com/methods/conversations.join
conversations.rename
https://api.slack.com/methods/conversations.rename
ハマったポイント
主に下記のポイントでぐぬぬとなっていた。特にcursor指定はちょっとまだ腹持ちしてないというか、理解が浅い。
チャンネルIDを取得する際のcursor指定(ページャ、pagination)
mapかforEachか引数二つの関数を呼び出すとき
renameのエラー "error": "not_authorized" からのuserOAuthToken
1.チャンネルIDを取得する際のcursor指定(ページャ、pagination)
参考にしたブログ
https://shiimanblog.com/engineering/public-channels/
limit1000で1000件までしか取得してないけど、チャンネル1000件以上あるとcursorを工夫する必要がある。
1000件までしか取得できないからそれ以上取得するときは、cursorで指定する。
cursorは一回目取得したときのオブジェクトの中にかいてある。
“response_metadata”: {
“next_cursor”: ” XXXXXX=”
}
https://api.slack.com/docs/pagination
2.mapかforEachか、引数二つの関数を呼び出すとき
チャンネルIDをチャンネル名称を二つの引数としたいときに、あーどうしよってちょっと悩んだ。配列で解決した。オブジェクトで解決する方法もある。それは後述する。
3.renameのエラー "error": "not_authorized" からのuserOAuthToken
最初はID取得と同じようにBotトークンで実行していた。
GAS上では特にエラーも出ずに実行完了していたが、名称が変わらず、あれー?となっていた。
で、こういう場合は、まずTesterで再チェックと思い、テストしてみたところ、
https://api.slack.com/methods/conversations.rename/test
だった。
APIの説明をあらためて読んでいくと
管理権限を持ったユーザでないとRenameできない、と。
ここで、BotトークンからUserトークンに切り替え、成功となった。
やってみた
1.名称変更したいチャンネルのIDを取得する
https://shiimanblog.com/engineering/public-channels/ を参考の上、改変したコードを用いた。
アナリティクスからチャンネルIDもDLできれば良かったのだが、
チャンネルIDはDLできないのでGASで取得する。
上のプランだとDLできるのか?
参考 Slack アナリティクスダッシュボードを閲覧する
APIは conversations.list を利用し、チャンネルIDを取得する。
conversations.info はチャンネルIDがRequired arguments なので、今回のようにそもそもIDを取得したいねんという時には不向き。
また、ハマったポイントにも書いたように、このconversations.list を利用してチャンネルIDを取得するときに、1000件以上のチャンネル数が存在しているとpaginationに気を付ける必要がある。
自分の雑な理解だと、ワークスペースの情報を持っていて、そこに仮想的にページが割り付けられており、工夫しないとその最初のページの情報しか取ってこれない事になる。その工夫がcursorの指定になる。
2.名称変更したいチャンネルにBotを参加させる→名称変更は管理者権限で行うからいらんかった
最初はbotに各チャンネルに参加させて、botにRenameしてもらおうと考えていた。で、先にこのjoinのコード書いたんだけど、結局これはいらんのよね。供養のためにここにUPしてみる。Botはちゃんと各チャンネルに参加できました。これをconversations.leaveに書き換えれば、チャンネルから去っていきます。
function setJoinChannels() {
const lastRow = targetSheet.getLastRow();
const values = targetSheet.getRange(1, 1, lastRow, 1).getValues();
const channelIDs = values.flat();
channelIDs.forEach(channelID => joinChannel(channelID));
}
function joinChannel(channelID) {
const options = {
"method": "post",
"contentType": "application/x-www-form-urlencoded",
"payload": {
"token": botUserOAuthToken,
"channel": channelID
}
}
const url = 'https://slack.com/api/conversations.join';
UrlFetchApp.fetch(url, options);
}
3.名称変更を行う
スプレッドシートの「名称変更対象」シートはこんな感じで1列目にID、2列目に変更後の名称としています。
values.forEach(value => renameChannel(value[0], value[1]));
上記のように配列で引数に渡す形にしたが、
別解として、こんな提案も。
オブジェクトでこうするのかあ、なるほど。
function setRenameChannels() {
const lastRow = targetSheet.getLastRow();
const values = targetSheet.getRange(1, 1, lastRow, 2).getValues();
const channelInfos = values.map(record => {
const channelInfo = { id: record[0], name: record[1] };
return channelInfo;
});
channelInfos.forEach(channelInfo => renameChannel(channelInfo.id, channelInfo.name));
}
スプレッドシートの値をオブジェクト化するときはこちらの情報が役立ちます。
なんやかんやオブジェクト使うからなあ。
ひとまず、今日はこんなとこで(雑)!
いただいたサポートで、書籍代や勉強費用にしたり、美味しいもの食べたりします!