【Python】Slackにメッセージとファイルを同時に送信
【Python】Slackにメッセージとファイルを同時に送信
はじめに
Slackは現代のビジネスコミュニケーションに欠かせないツールとなっています。チームでの情報共有やプロジェクト管理において、テキストメッセージだけでなくファイルの共有も頻繁に行われます。この記事では、PythonからSlack APIを使用して、メッセージとファイルを同時に送信する方法を解説します。
自動化スクリプトやバッチ処理の結果をSlackに通知したい、定期的なレポートファイルを特定のチャンネルに自動投稿したいなど、様々なユースケースで活用できること間違いなしです!
前提条件
この記事のコードを実行するためには、以下の準備が必要です:
- Python 3.6以上の環境
- 必要なパッケージのインストール:
requests
- Slack APIトークンの取得
- 投稿先のSlackチャンネルID
# requestsのインストール
pip install requests
追加の機能を使用する場合は、以下のパッケージも必要になります:
# 環境変数管理用
pip install python-dotenv
# データ分析例用
pip install pandas matplotlib
Slack APIトークンの取得方法
Slack APIを利用するには、APIトークンが必要です。以下の手順で取得できます:
- Slack API公式サイトにアクセス
- 「Create New App」をクリック
- アプリ名とワークスペースを選択
- 「OAuth & Permissions」セクションで以下の権限(スコープ)を追加:
files:write
chat:write
- アプリをワークスペースにインストール
- 「Bot User OAuth Token」(
xoxb-
で始まるもの)をコピー
詳細はこちらの記事を参考👇️
コードの全体像
まずは完全なコードを見てみましょう。このコードは、メッセージとファイルを同時にSlackに送信する機能を提供します。
import requests
import json
import os
class Slack:
def __init__(self, token):
self.token = token
def upload_file_to_slack(self, channel_id, message=None, file_name=None, file_path=None):
"""
Slackにファイルまたはメッセージを投稿する関数
:param channel_id: 投稿先のチャンネルID
:param message: メッセージ(任意)
:param file_name: ファイル名(任意)
:param file_path: ファイルパス(任意)
:return: API応答のJSONデータ
:raises FileNotFoundError: ファイルが見つからない場合
:raises ValueError: メッセージもファイルも指定されていない場合
:raises Exception: API呼び出しが失敗した場合
"""
if file_path:
# ファイルがある場合
try:
with open(file_path, 'rb') as f:
file_blob = f.read()
except Exception as e:
raise FileNotFoundError(f"ファイルが見つかりません: {file_path}") from e
file_size = len(file_blob)
# アップロードURL取得
params = {
'filename': file_name,
'length': file_size
}
headers = {
'Authorization': f'Bearer {self.token}'
}
upload_url_response = requests.get('https://slack.com/api/files.getUploadURLExternal', params=params, headers=headers)
upload_url_json = upload_url_response.json()
if not upload_url_json.get('ok', False):
raise Exception(f"アップロードURLの取得に失敗しました: {upload_url_json.get('error')}")
upload_url = upload_url_json['upload_url']
file_id = upload_url_json['file_id']
# ファイルをアップロード
upload_headers = {
'Content-Type': 'application/octet-stream'
}
upload_response = requests.post(upload_url, headers=upload_headers, data=file_blob)
if upload_response.status_code != 200:
raise Exception(f"ファイルのアップロードに失敗しました: {upload_response.text}")
# アップロード完了通知
complete_upload_payload = {
'channel_id': channel_id,
'files': [{'id': file_id, 'title': file_name}]
}
if message:
complete_upload_payload['initial_comment'] = message # Slack APIは initial_comment というキー
complete_upload_headers = {
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json'
}
complete_upload_response = requests.post(
'https://slack.com/api/files.completeUploadExternal',
headers=complete_upload_headers,
json=complete_upload_payload
)
complete_upload_json = complete_upload_response.json()
if not complete_upload_json.get('ok', False):
raise Exception(f"アップロード完了通知に失敗しました: {complete_upload_json.get('error')}")
print("[アップロード] ファイルのアップロードが完了しました。")
return complete_upload_json
else:
# ファイルがない場合は単純なメッセージ送信
if message is None:
raise ValueError("file_path も message も両方Noneです。何も投稿するものがありません。")
chat_payload = {
'channel': channel_id,
'text': message
}
chat_headers = {
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json'
}
chat_response = requests.post(
'https://slack.com/api/chat.postMessage',
headers=chat_headers,
json=chat_payload
)
chat_json = chat_response.json()
if not chat_json.get('ok', False):
raise Exception(f"メッセージの送信に失敗しました: {chat_json.get('error')}")
print("[投稿] メッセージの送信が完了しました。")
return chat_json
if __name__ == "__main__":
# 環境変数からトークンを読み込む例
# token = os.environ.get("SLACK_API_TOKEN")
# テスト用(実際の使用時は上記の環境変数を使用することを推奨)
token = "YOUR_SLACK_API_TOKEN" # ここに自分のトークンを設定
slack = Slack(token)
slack.upload_file_to_slack(
channel_id='CHANNEL_ID', # ここにチャンネルIDを設定
message="ファイルをアップロードします",
file_name="sample.png",
file_path="path/to/sample.png" # 実際のファイルパスに変更
)
⚠️ セキュリティ上の重要な注意: 実際のコードではAPIトークンをハードコーディングせず、環境変数などから安全に読み込むようにしましょう。トークンが漏洩すると、第三者があなたのSlackワークスペースにアクセスできてしまう可能性があります。
コードの詳細解説
1. Slackクラスの初期化
class Slack:
def __init__(self, token):
self.token = token
Slackクラスは初期化時にAPIトークンを受け取ります。このトークンは後続のAPI呼び出しで認証に使用されます。
2. ファイルアップロードのフロー
Slack APIを使ったファイルアップロードは、3つの主要なステップで構成されています:
- アップロードURLの取得
- ファイルの実際のアップロード
- アップロード完了の通知
これらのステップについて詳しく見ていきましょう。
2.1 アップロードURLの取得
# アップロードURL取得
params = {
'filename': file_name,
'length': file_size
}
headers = {
'Authorization': f'Bearer {self.token}'
}
upload_url_response = requests.get('https://slack.com/api/files.getUploadURLExternal', params=params, headers=headers)
upload_url_json = upload_url_response.json()
if not upload_url_json.get('ok', False):
raise Exception(f"アップロードURLの取得に失敗しました: {upload_url_json.get('error')}")
upload_url = upload_url_json['upload_url']
file_id = upload_url_json['file_id']
ここでは、files.getUploadURLExternal
エンドポイントを呼び出して、一時的なアップロードURLとファイルIDを取得しています。このURLは次のステップでファイルをアップロードするために使用されます。
2.2 ファイルのアップロード
# ファイルをアップロード
upload_headers = {
'Content-Type': 'application/octet-stream'
}
upload_response = requests.post(upload_url, headers=upload_headers, data=file_blob)
if upload_response.status_code != 200:
raise Exception(f"ファイルのアップロードに失敗しました: {upload_response.text}")
取得したURLに対して、ファイルの内容(バイナリデータ)をPOSTリクエストで送信します。ヘッダーにはContent-Type: application/octet-stream
を指定しています。
2.3 アップロード完了通知
# アップロード完了通知
complete_upload_payload = {
'channel_id': channel_id,
'files': [{'id': file_id, 'title': file_name}]
}
if message:
complete_upload_payload['initial_comment'] = message # Slack APIは initial_comment というキー
complete_upload_headers = {
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json'
}
complete_upload_response = requests.post(
'https://slack.com/api/files.completeUploadExternal',
headers=complete_upload_headers,
json=complete_upload_payload
)
最後に、files.completeUploadExternal
エンドポイントを呼び出して、ファイルのアップロードを完了し、指定されたチャンネルに投稿します。このとき、オプションのメッセージ(initial_comment
)を付けることができます。これにより、ファイルとメッセージが同時に送信されます。
3. メッセージのみの送信
# ファイルがない場合は単純なメッセージ送信
if message is None:
raise ValueError("file_path も message も両方Noneです。何も投稿するものがありません。")
chat_payload = {
'channel': channel_id,
'text': message
}
chat_headers = {
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json'
}
chat_response = requests.post(
'https://slack.com/api/chat.postMessage',
headers=chat_headers,
json=chat_payload
)
ファイルパスが指定されていない場合は、chat.postMessage
エンドポイントを使用して、テキストメッセージのみを送信します。
使用例
モジュールとして使用するための準備
まずは、上記のコードをslack_uploader.py
として保存しましょう。これにより、他のPythonスクリプトからインポートして使用できるようになります。
基本的な使用方法
import os
from slack_uploader import Slack
# 環境変数からトークンを取得
token = os.environ.get("SLACK_API_TOKEN")
# トークンが取得できない場合のフォールバック(実際の運用では使用しないでください)
if not token:
token = "xoxb-your-token" # 環境変数から取得するなど安全な方法で
# Slackインスタンスの作成
slack = Slack(token)
# メッセージとファイルを送信
slack.upload_file_to_slack(
channel_id='C01234ABCDE',
message="月次レポートを共有します",
file_name="monthly_report.pdf",
file_path="./reports/monthly_report.pdf"
)
# メッセージのみを送信
slack.upload_file_to_slack(
channel_id='C01234ABCDE',
message="明日の会議は15時からです"
)
注意点
APIトークンの安全な管理
APIトークンはソースコードにハードコーディングせず、環境変数や設定ファイルから安全に読み込むようにしましょう。特に公開リポジトリにコードをアップロードする場合は注意が必要です。
import os
from dotenv import load_dotenv
# .envファイルから環境変数を読み込み
load_dotenv()
# 環境変数からトークンを取得
token = os.environ.get('SLACK_API_TOKEN')
if not token:
raise ValueError("SLACK_API_TOKENが設定されていません")
.env
ファイルの例:
SLACK_API_TOKEN=xoxb-your-token-here
SLACK_CHANNEL_ID=C01234ABCDE
このファイルは.gitignore
に追加して、バージョン管理システムでの追跡から除外してください。
API制限への対応
Slack APIには以下の制限があります:
- レート制限: Tier 3のアプリは50リクエスト/分の制限があります。
- ファイルサイズ制限: 1ファイルあたり最大50MBまで。
- チャネルあたりのファイル数: 保存期間によって制限が異なります。
ファイルサイズの確認
大きなファイルをアップロードする前に、サイズを確認することをお勧めします。
def check_file_size(file_path, max_size_mb=50):
"""ファイルサイズを確認し、制限を超えている場合はFalseを返す"""
max_size_bytes = max_size_mb * 1024 * 1024 # MBからバイトに変換
try:
file_size = os.path.getsize(file_path)
if file_size > max_size_bytes:
logging.warning(
f"ファイルサイズが制限を超えています: {file_size / (1024 * 1024):.2f}MB "
f"(最大: {max_size_mb}MB)"
)
return False
return True
except OSError as e:
logging.error(f"ファイルサイズの確認中にエラーが発生しました: {e}")
return False
リッチメッセージの送信
Slackでは、メッセージに特殊なフォーマットを適用できます。これを活用すると、より見やすく情報を伝えることができます。
基本的なフォーマット
-
*太字*
→ 太字 -
_イタリック_
→ イタリック -
~取り消し線~
→取り消し線 -
`コード`
→コード
-
コードブロック
→ コードブロック
リストとブロック引用
• 箇条書きリスト
> 引用テキスト
絵文字と特殊リンク
- 絵文字:
:smile:
→ 😊 - リンク:
<https://example.com|テキスト>
→ クリック可能なリンク - チャンネル:
<#C01234ABCDE>
→ チャンネルへのリンク - ユーザー:
<@U01234ABCDE>
→ ユーザーへのメンション
詳細なフォーマットガイドはSlack APIドキュメントを参照してください。
よくあるエラーとその対処法
-
認証エラー (
not_authed
,invalid_auth
,token_revoked
)- APIトークンが無効または失効している可能性があります。新しいトークンを取得してください。
-
スコープ不足 (
missing_scope
)- トークンに必要な権限(スコープ)が設定されていません。アプリの設定で
files:write
とchat:write
を追加してください。
- トークンに必要な権限(スコープ)が設定されていません。アプリの設定で
-
チャンネルが見つからない (
channel_not_found
)- 指定したチャンネルIDが無効か、ボットがそのチャンネルに招待されていない可能性があります。
- チャンネルIDを確認し、必要に応じてボットをチャンネルに招待してください。
-
レート制限 (
rate_limited
)- 短時間に多くのリクエストを送信しすぎています。
- エラーメッセージに含まれる
Retry-After
の値(秒)だけ待機してから再試行してください。
-
ファイルサイズ超過 (
file_too_large
)- ファイルサイズが上限(50MB)を超えています。
- ファイルを分割するか、圧縮してサイズを小さくしてください。
まとめ
この記事では、PythonからSlack APIを使ってメッセージとファイルを同時に送信する方法を紹介しました。以下のポイントを押さえることで、効果的なSlack通知システムを構築できます:
- Slack APIの認証とトークン管理
- ファイルアップロードの3ステップ(URL取得、アップロード、完了通知)
- メッセージとファイルの同時送信
- エラーハンドリングとレート制限への対応
- セキュリティ対策(トークン管理、ファイルサイズチェック)
このコードを活用して、チームのコミュニケーションを効率化し、様々な自動化を実現してください。
※筆者は、機械学習プログラムの結果を随時送信することで、正常に学習が進んでいるかを監視するために利用しています。
参考リソース
- Slack API公式ドキュメント
- files.getUploadURLExternal
- files.completeUploadExternal
- chat.postMessage
- Slack メッセージフォーマットガイド
この記事が皆さんのプロジェクトやタスクの自動化に役立つことを願っています。
質問やフィードバックがありましたら、コメントをお待ちしています。
Discussion