Slack Botの自動投稿は、外部のスケジューラからchat.postMessageを呼んで毎朝や毎週の決まった時刻に流す方法と、chat.scheduleMessageでSlack側に予約を登録しておく方法の2系統に分かれます。この記事では、slack_sdkを使ったPythonの投稿コード、予約投稿の上限と取り消しの手順、EventBridge SchedulerとGitHub Actionsで定時に起動する設定例を、Slack公式ドキュメントの記述に沿って順に示します。slack bot リマインダーとして日報の催促や定例の案内を流したい方向けに、リトライで同じ通知が2回出ない作りと、ワークフロービルダーで済ませてよい場面の線引きもまとめました。
まとめ:Slack Botの自動投稿で最初に決める送信方式と起動元と重複の防ぎ方
毎日や毎週くり返す通知は、Bot Tokenでchat.postMessageを呼ぶ小さなプログラムを書き、EventBridge SchedulerやGitHub Actionsから定時に起動します。投稿文を実行時に組み立てられるので、集計値や担当者名を差し込めます。
日時が決まった単発の告知はchat.scheduleMessageで予約します。予約は120日先まで、同じチャンネルの同じ5分間には30件までです。
起動元は失敗時に再実行するため、投稿済みの記録を残して二重投稿を防ぎます。固定文を流すだけならワークフロービルダーで足ります。
Slack Botで自動投稿する4つの方法と定期投稿・予約・リマインダーの違い
ワークフロー・リマインダー・Webhook・Bot APIで分かれる自動投稿の手段
Slackへ決まった時刻に投稿する手段は4つあり、誰が投稿者になるかと、文面を実行時に変えられるかで分かれます。
| 手段 | 投稿者 | 文面の可変 | 向く用途 |
|---|---|---|---|
| ワークフロービルダー | ワークフロー | 変数の範囲 | 固定の定例案内 |
| リマインダー | Slackbot | 不可 | 個人の備忘 |
| Incoming Webhook | アプリ | 可 | 監視の通知 |
| Bot API | ボットユーザー | 可 | 集計や予約の投稿 |
Incoming Webhookは送信専用で、投稿後にメッセージのtsが返りません。定時の通知を後から更新したり、同じスレッドへ続けて返信したりするなら、Bot APIを選ぶのが、この操作に対応する方法です。Webhookの実装はSlack Incoming Webhookの実装手順にまとめています。メンションに応答するBotの作り方はSlack Botの作り方:Bolt for Pythonでメンション返信からLambda運用までで扱い、この記事は投稿時刻の制御に絞ります。
リマインダーAPIは2023年3月から退役が進みBotの代わりにならない理由
「/remind」のリマインダーをAPIから登録しようとすると、壁に当たります。reminders.addのリファレンスには、リマインダー用のAPIメソッドは2023年3月から退役が始まり、機能が落ちているか使えない状態だと書かれています。他のユーザー宛てに設定するuser引数も、すでに非対応です。
新しく作る仕組みでリマインダーAPIに頼るのはやめておきます。チャンネルへの催促は、Botがchat.postMessageで投稿する形に置き換えるのが確実です。個人が自分用に使う「/remind me」は従来どおり手で設定できるので、Botで置き換える対象はチーム宛ての定期連絡に限られます。
chat.postMessageで定時に投稿するBotをPythonで動かす実装の手順
chat:writeを付けてBotをチャンネルへ招待するまでの事前準備の手順
定期投稿に要る権限は、Bot Tokenスコープのchat:writeだけです。新しいSlackアプリは、すべてのパブリックチャンネルへ投稿できる状態では始まりません。投稿先のチャンネルでアプリを追加するか、招待せずに投稿したい場合だけchat:write.publicを足します。
- アプリ管理画面のOAuth & Permissionsで、Bot Token Scopesに
chat:writeを追加する - ワークスペースへインストールし、xoxbで始まるBot Tokenを控える
- 投稿先チャンネルの詳細からアプリを追加し、チャンネルIDを控える
チャンネルは名前より、Cで始まるIDで指定します。名前を変えられても投稿先がずれません。トークンの発行と保管はSlack Bot Tokenの取得と確認に手順があります。
slack_sdk 3.46.0:chat_postMessageのPython投稿例
Slack公式のPython SDKであるslack-sdkは、2026年10月30日公開の3.46.0が最新です(2026年11月時点)。次のコードは、実行した日の日付を入れて日報の締め切りを知らせます。スケジューラからはこのファイルを起動するだけです。
import os
from datetime import datetime
from zoneinfo import ZoneInfo
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
client = WebClient(token=os.environ["SLACK_BOT_TOKEN"])
CHANNEL_ID = os.environ["SLACK_CHANNEL_ID"] # Cから始まるチャンネルID
def post_daily_reminder():
today = datetime.now(ZoneInfo("Asia/Tokyo")).strftime("%m/%d")
try:
res = client.chat_postMessage(
channel=CHANNEL_ID,
text=f"{today} の日報は18時までに提出してください",
)
return res["ts"]
except SlackApiError as e:
# not_in_channel・missing_scope・ratelimited などはここで受ける
print("投稿失敗:", e.response["error"])
raise
if __name__ == "__main__":
post_daily_reminder()
戻り値のtsを保存しておくと、あとでchat.updateによる訂正やスレッド返信の親に使えます。失敗時に例外を投げ直しているのは、起動元に失敗を伝えて再実行や通知の対象にするためです。
1チャンネル毎秒1件の制限とnot_in_channelで止まるときの切り分け
chat.postMessageのリファレンスでは、1つのチャンネルへの投稿は概ね1秒に1件、ワークスペース全体では毎分数百件が上限とされ、短いバーストは許容されます。部署ごとに数十チャンネルへ同時に流す程度なら届きますが、同じチャンネルへ連投するループは1秒待つ作りにします。上限の考え方を確認する参照先はRate limitsのページです。
初回に多いエラーはnot_in_channelです。Botがそのチャンネルに入っていないときに返るので、アプリの追加を確かめます。missing_scopeなら、スコープ追加後の再インストールが済んでいません。本文は4,000字以内に抑えるのが推奨で、40,000字を超えると切り詰められます。
chat.scheduleMessageでSlack側に予約投稿を登録して管理する手順
post_atは120日先まで・5分30件までという予約登録の上限と例外
chat.scheduleMessageのリファレンスによると、予約時刻のpost_atはUnix秒の整数で、120日先までしか指定できません。超えるとtime_too_far、過去の時刻ならtime_in_pastが返ります。同じチャンネルで同じ5分間に投稿される予約が30件を超えると、restricted_too_manyで拒否されます。
予約が向くのは、日時の決まった告知です。登録したあとはBot側のサーバーが止まっていても、Slackが指定時刻に投稿します。既存スレッドへの予約返信に使う指定はthread_tsです。注意点もあり、同じページにはmetadata引数を付けると投稿されない不具合が記載されています。予約にはメタデータを付けないでおきます。
同じトークンの予約だけを一覧で取得し60秒前までに取り消す手順
登録した予約はscheduled_message_idで管理します。次のコードは、翌週月曜9時(日本時間)に定例の案内を予約し、一覧を確かめてから取り消す一連の流れです。
import os
from datetime import datetime
from zoneinfo import ZoneInfo
from slack_sdk import WebClient
client = WebClient(token=os.environ["SLACK_BOT_TOKEN"])
CHANNEL_ID = os.environ["SLACK_CHANNEL_ID"]
# 予約時刻はUnix秒の整数で渡す(2026年11月9日 月曜 9:00 JST)
post_at = int(datetime(2026, 11, 9, 9, 0, tzinfo=ZoneInfo("Asia/Tokyo")).timestamp())
res = client.chat_scheduleMessage(
channel=CHANNEL_ID,
post_at=post_at,
text="週次定例は10時からです。議題はこのスレッドへ",
)
scheduled_id = res["scheduled_message_id"]
# 同じトークンで登録した予約だけが返る(Slackの画面で予約した分は含まれない)
listed = client.chat_scheduledMessages_list(channel=CHANNEL_ID)
for m in listed["scheduled_messages"]:
print(m["id"], m["post_at"], m["text"])
# 取り消し。投稿まで60秒を切ると invalid_scheduled_message_id になる
client.chat_deleteScheduledMessage(channel=CHANNEL_ID, scheduled_message_id=scheduled_id)
chat.scheduledMessages.listは、同じトークンでchat.scheduleMessageから登録した予約しか返さず、画面から予約した分は含みません。chat.deleteScheduledMessageは、送信済みの分と、60秒以内に投稿される分を消せません。予約を後から編集するメソッドは用意されていないため、文面の訂正は削除して登録し直します。
cronとサーバーレスで定期投稿を起動するEventBridgeとActionsの設定例
EventBridge Schedulerによる平日9時のLambda起動設定
AWSで動かすなら、投稿コードをLambda関数に置き、EventBridge Schedulerから起動します。Schedule typesのページのとおり、cron式は分・時・日・月・曜日・年の6項目で、IANAのタイムゾーンを指定できます。Asia/Tokyoを指定すれば、UTCへの換算は不要です。
aws scheduler create-schedule \
--name slack-daily-report \
--schedule-expression "cron(0 9 ? * MON-FRI *)" \
--schedule-expression-timezone "Asia/Tokyo" \
--flexible-time-window '{"Mode": "OFF"}' \
--target '{"Arn": "arn:aws:lambda:ap-northeast-1:123456789012:function:slack-post",
"RoleArn": "arn:aws:iam::123456789012:role/scheduler-invoke-slack-post",
"RetryPolicy": {"MaximumRetryAttempts": 2, "MaximumEventAgeInSeconds": 600}}'
起動の精度は60秒で、9:00の指定なら9:00:00から9:00:59の間に呼ばれます。RetryPolicyの上限は185回・86,400秒です。朝の通知が昼に届いても意味がないため、例では再試行を2回・600秒に絞りました。手動実行や失敗の監視はEventBridge Scheduler APIの使い方で扱っています。
GitHub Actionsのschedule:UTC既定・毎時0分遅延・60日停止
サーバーを持たない小さなチームなら、GitHub Actionsのscheduleでも定期投稿を起動できます。GitHub Docsのイベント一覧によると、最短間隔は5分で、既定はUTCです。timezoneキーでIANAのタイムゾーンを指定できます。
name: slack-daily-report
on:
schedule:
- cron: '7 9 * * 1-5'
timezone: "Asia/Tokyo"
workflow_dispatch:
jobs:
post:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install slack-sdk==3.46.0
- run: python post_daily.py
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }}
同じページには、高負荷時にscheduleが遅れ、ジョブが捨てられる場合もあると書かれています。毎時0分は高負荷の時間帯に含まれるので、例では9時7分にずらしました。デフォルトブランチでしか動かず、公開リポジトリは60日間活動が無いと自動で無効になります。時刻の正確さが要る通知には向きません。Actions全体の仕組みはGitHub Actionsとは?を参照してください。
リトライで同じ通知が2回出るのを防ぐ投稿済み記録の置き方と判定
再試行で気を付けたいのは、投稿成功後の処理で失敗する場合です。Slackへの投稿は成功したのに、その後の処理でLambdaがタイムアウトすると、Schedulerは失敗とみなして再び起動します。結果として、同じ日報の催促がチャンネルに2回並びます。
防ぎ方は、投稿の前に「この日付・このチャンネルの通知を出したか」を記録で確かめることです。DynamoDBなら、日付とチャンネルIDを組み合わせたキーで条件付き書き込みを行い、書けたときだけ投稿します。投稿後に返ったtsも同じ項目へ残すと、後から訂正するときに使えます。受け手側で重複を弾く考え方はWebhookの作り方と実装方法の冪等性の章と同じです。
自動投稿をBotで自作するかワークフローやiPaaSで済ませるかの判断基準
ワークフロービルダーの定期トリガーで足りる定型連絡と自作を見送る条件
文面が毎回同じ定例の案内なら、Botは作りません。ワークフロービルダーのスケジュールトリガーで足り、サーバーもトークン管理も要らないからです。設定の手順はSlackワークフローの作り方にあります。
自作が過剰になる条件ははっきりしています。投稿先が1〜2チャンネルで、文面に外部のデータを入れず、止まっても業務が回る通知です。この3つがそろうなら、保守する人がいなくなったときに止まるコードを抱えるより、ビルダーのほうが安全です。
社内データを集計して流すなら自作Botを選ぶ条件とiPaaSの使い分け
Botを書く価値が出るのは、投稿のたびに社内システムのデータを読む場合です。前日の受注件数、未承認の申請一覧、期限切れ間近の契約といった中身を、DBや業務システムから集計して流すなら、chat.postMessageを呼ぶ自作の処理が向きます。予約した告知を一覧で管理し、取り消しまで自動化したい場合も同じです。
集計元がkintoneやSalesforceなどのSaaSで、条件も単純なら、iPaaSのスケジュール実行で組む選択肢があります。コードの保守を持たずに済む反面、実行回数で料金が増えます。iPaaSとRPAの違いはiPaaSとは?で整理しました。どちらで組むか決めかねている場合は、iPaaS導入支援(Zapier/Make/Workato/Yoom)で、連携先と通知の要件から構成を相談できます。
よくある質問
Slack Botの自動投稿や予約投稿について、実装の前によく確認される点をまとめました。
Slack Botの予約投稿はあとから編集できますか?
編集はできません。Slackの公式リファレンスに、予約メッセージを書き換えるメソッドは載っていません。用意されているのは一覧の取得と削除だけです。文面を直したいときは、chat.deleteScheduledMessageで予約を消し、chat.scheduleMessageで登録し直します。投稿まで60秒を切った予約は消せないので、訂正が必要になりそうな告知は時間に余裕を持って登録しておくと安心です。
土日や祝日を除いて自動投稿するにはどう設定しますか?
土日を投稿対象から除く方法は、cron式での曜日指定です。EventBridge Schedulerならcron(0 9 ? * MON-FRI *)、GitHub Actionsなら'7 9 * * 1-5'のように曜日を絞ります。祝日はcron式では表せないため、投稿コードの先頭で祝日の一覧と照合し、該当日は投稿せずに処理を終了させる作りです。一覧は年に一度更新する運用にし、会社独自の休業日もそこへ足しておきます。
Incoming WebhookとBotでは定期投稿にどちらを使うべきですか?
定期投稿の起動元がある前提なら、どちらでも投稿はできます。決め手は投稿後の操作です。Webhookは応答にtsが返らないため、送った通知の訂正やスレッドへの追記ができません。前回の投稿を更新したい、返信を同じスレッドにまとめたい、予約投稿も使いたいといった場合はBotにします。送りっぱなしの監視通知だけならWebhookで十分です。
自動投稿の時刻が数分ずれるのはなぜですか?
起動元の仕様によるものです。GitHub Actionsのscheduleは高負荷時に遅れることがあり、特に混雑する時間帯は毎時0分です。EventBridge Schedulerは60秒の精度で起動し、柔軟な時間枠を設定するとその幅の中で起動時刻が動きます。分単位の正確さが要るなら、柔軟な時間枠をOFFにしたEventBridge Schedulerか、Slack側に時刻を登録するchat.scheduleMessageを選びます。
同じスレッドへ毎日自動で返信を続けられますか?
同じスレッドへの継続的な返信は可能です。最初の投稿で返ったtsを保存し、以降のchat.postMessageやchat.scheduleMessageでthread_tsに指定します。月初に親メッセージを立て、日々の数値をその下へ返信する作りにすると、チャンネルの流れを日々の返信で埋めずに済む構成です。reply_broadcastを付けると、返信をチャンネルにも表示できます。
関連記事
- Slack APIとは?Web API・Events API・Boltの違いと実装手順:投稿に使うWeb APIと、イベントを受けるEvents APIの全体像
- Slackワークフローの業務別の使い方10例:日報や定例案内をノーコードで回す場合の組み方
- スラッシュコマンドとは?Slack・Discord・AIツールの違いとSlackでの自作手順:定時投稿に加えて、利用者がコマンドで呼び出す入口を足す場合