名称未設定_1

pythonでSlackチャンネル名を取得してみた

少し前から勉強してきたプログラム言語「python」で、普段仕事で使っているコミュニケーションツール「Slack」のチャンネル名を取得するプログラムを作ってみました。

(背景)pythonの理解を深める&省エネ主義

・定期的にslackのチャンネル名をエクセルシートに写すの面倒
・標準でエクスポート機能はあるけどそれもなんか面倒っぽい
・とりあえず学んだpythonでアウトプットしたかった
・他の方の「古いファイル削除プログラム」に感動して内容を理解したかった

最後の2つが特に大きくて、こちらの方「Slackで不要になった昔のファイルを削除する」プログラムを動かしてみたのがきっかけです。

こちらの方のコードをベースに、チャンネル名一覧を取得するプログラムを作ることで勉強できる!と思った次第。

(作ったもの)直近のチャンネル名をAPIで取得する

上述の通り、チャンネル名のコピペを繰り返すのがなんか現代人らしくなくていやだ&面倒だったので、実行すれば「参加している直近30日のチャンネル名」を取得してくるものを作りました。といっても使ったのはこのメソッドだけでライブラリもRequestsくらい。

(事前準備・前提知識)

・python基礎知識と実行環境→こちらを参考ください
・Slack APIを使うためのトークン取得→こちらを参考ください

(工夫したこと)「カーソル」というページ概念

基本的には上述の方のコードを元に作りましたが、pythonルーキーながら諸々自分で苦労してみました。

APIを使って取得してきたデータに全部のチャンネルが載っているわけではなく、データはいくつかのページのように分かれています。(便宜上ページと呼ぶ)

ここで問題だったのは「カーソル」という値が割り振られていて、データの最後に「次のカーソル」が記載されている仕組み。ページ1〜最終ページまでと数えていく処理ができない。そこでこんな感じで次のカーソルを取得させる関数を置く。
スリープ(待機時間)入れてるのは連続で短期間にAPIを叩かない配慮。

# -------------------------------
# 次のカーソルをセット
# -------------------------------


def getCursorFrom(data):
   cursor = data['response_metadata']['next_cursor']
   sleep(3)
   return cursor

さらに詰まったのはこの仕様↓。なのでこうしました。

・最初のページを取得する際はカーソルを指定しないURLでデータ取得
・二つ目以降はカーソルを指定したURLでデータ取得
・最終ページに載っている次のカーソルは""(空白)で記載されている​
# --------------------------------------
# カーソルから次のデータへ移行(cursor代入は初回実行のため)
# --------------------------------------
cursor = 'first'
cursor_url = ''

while cursor != '':
   getDataFromApi(cursor_url)
   appendChannelFrom(getDataFromApi(cursor_url))
   cursor = getCursorFrom(getDataFromApi(cursor_url))
   cursor_url = 'cursor={}'.format(cursor)

まずカーソルに適当な文字入れておいて、カーソルを指定するURL「cursor_url」は空にしておく。
あとは最終ページで空の「次のカーソル」が出てくるまで
①「getDataFromApi」でデータ取得→
②「appendChannelFrom」でそのデータからチャンネル名をリストに入れていく→
③「getCursorFrom」でそのデータから「次のカーソルの値」を取得→
④変数「cursor_url」に次のデータへアクセスするURLをいれる、とループ。
なんか美しくないし初心者感がすごいけど試行錯誤した中で一番これが短いコードだった。

(工夫したこと)自分を含むチャンネルに限定

まったく工夫でもなんでもないけど、公式説明でこう書いてあったので「自分を含む」か否かをこの値で判断してます(チャンネル名取得する関数の中)。ちなみにもちろん自分がワークスペースの管理者です。チャンネル制作日の説明もこちら。

is_member
indicates the user or bot user or Slack app associated with the token making the API call is itself a member of the conversation. 

creator is the user ID of the member that created this channel. created is a unix timestamp.

conversation typeより
# -------------------------------------
# 対象チャンネル名を探して対象に追加
# -------------------------------------
def appendChannelFrom(data):
   now_date = datetime.now()
   channels = data['channels']
   for channel in channels:
       target_date = datetime.fromtimestamp(int(channel['created']))
       days = (now_date - target_date).days
       if days <= LAST_DAYS and channel['is_member'] == True:
           target_names.append(channel['name_normalized'])

あとはため込んだ名前のリストをはきだしていくだけ。

(完成形)

上述の通りこちらの方のコードを元にしています。使用は自己責任でお願いします。

# coding: UTF-8
#!/usr/bin/python3

import requests
import sys
from time import sleep
from datetime import datetime

# Need Legacy Token
TOKEN = ''
LAST_DAYS = 31

# ------------------------
# APIから対象カーソルのデータを取得
# ------------------------

def getDataFromApi(cursor_url):
   url = 'https://slack.com/api/conversations.list?token={0}&{1}&pretty=1'.format(TOKEN,cursor_url)
   response = requests.get(url)
   data = response.json()
   return data

target_names = []

# ------------------------
# 対象チャンネル名を探して対象に追加
# ------------------------
def appendChannelFrom(data):
   now_date = datetime.now()
   channels = data['channels']
   for channel in channels:
       target_date = datetime.fromtimestamp(int(channel['created']))
       days = (now_date - target_date).days
       if days <= LAST_DAYS and channel['is_member'] == True:
           target_names.append(channel['name_normalized'])

# ------------------------
# 次のカーソルをセット
# ------------------------

def getCursorFrom(data):
   cursor = data['response_metadata']['next_cursor']
   print('カーソル数確認中: 次カーソル:{}'.format(cursor))
   sleep(3)
   return cursor
   

# ------------------------
# カーソルから次のデータへ移行(cursor代入は初回実行のため)
# ------------------------
cursor = 'first'
cursor_url = ''

while cursor != '':
   getDataFromApi(cursor_url)
   appendChannelFrom(getDataFromApi(cursor_url))
   cursor = getCursorFrom(getDataFromApi(cursor_url))
   cursor_url = 'cursor={}'.format(cursor)

print('チャンネルは合計 {} 個'.format(len(target_names)))

# ------------------------
# 取得したチャンネル名を表示
# ------------------------

for name in target_names:
   print(name)

(まとめ)たいしたことしてないけど達成感はあった

参考元のコードを読み解かせてもらって、変更を加えるだけというほぼ何もしてないに近いけど、自分でコードを書き込むことですごく勉強になりました!やっぱりアウトプットに限る。

以上です!何かすこしでも役に立てば嬉しいです。
コメントもお待ちしてます。


この記事が気に入ったらサポートをしてみませんか?