投稿を予約する
ボットを常に動かしておく必要はありません。もっとも簡単で確実な構成は、数分おきに実行され、時間になったものを投稿して終了する スクリプトです。
時間になったものだけを投稿する
スクリプトに予定表と、決まった実行間隔を持たせます。各実行では、前回の実行以降に 時刻を迎えた項目を投稿します。実行が時間どおりに行われる限り、二重投稿も投稿漏れも起きません。
// scheduled.ts: 15 分ごとに実行します(cron、GitHub Actions など)。
// 各実行で前回以降に時刻を迎えたものを投稿するので、二重投稿は起きません。
import { AtpAgent } from "@atproto/api";
const RUN_EVERY_MS = 15 * 60 * 1000;
const SCHEDULE = [
{ at: "2026-11-02T08:00:00Z", text: "Good morning! Today's forecast is up." },
{ at: "2026-11-02T18:00:00Z", text: "Evening update: clear skies tonight." },
];
const now = Date.now();
const due = SCHEDULE.filter((post) => {
const at = Date.parse(post.at);
return at <= now && at > now - RUN_EVERY_MS;
});
if (due.length > 0) {
const agent = new AtpAgent({ service: "https://bsky.social" });
await agent.login({
identifier: process.env.BLUESKY_HANDLE ?? "",
password: process.env.BLUESKY_APP_PASSWORD ?? "",
});
for (const post of due) {
await agent.post({ text: post.text, createdAt: new Date().toISOString() });
console.log(`Posted: ${post.text}`);
}
} else {
console.log("Nothing due.");
}Python では次のようになります。
# scheduled.py: 15 分ごとに実行します(cron、GitHub Actions など)。
# 各実行で前回以降に時刻を迎えたものを投稿するので、二重投稿は起きません。
import os
from datetime import datetime, timedelta, timezone
from atproto import Client
RUN_EVERY = timedelta(minutes=15)
SCHEDULE = [
("2026-11-02T08:00:00+00:00", "Good morning! Today's forecast is up."),
("2026-11-02T18:00:00+00:00", "Evening update: clear skies tonight."),
]
now = datetime.now(timezone.utc)
due = [
text
for at, text in SCHEDULE
if now - RUN_EVERY < datetime.fromisoformat(at) <= now
]
if due:
client = Client()
client.login(os.environ["BLUESKY_HANDLE"], os.environ["BLUESKY_APP_PASSWORD"])
for text in due:
client.send_post(text)
print(f"Posted: {text}")
else:
print("Nothing due.")予定が変わる場合は、コードではなくファイルやデータベースから読み込み、投稿済みのものを記録しておきましょう。遅れた実行や重複した実行でも二重投稿を防げます。
cron で実行する
自分で管理しているサーバーなら、crontab -e で 1 行追加します(15 分ごと)。
*/15 * * * * cd /home/bot/my-bot && BLUESKY_HANDLE=weather.bluesky.bot BLUESKY_APP_PASSWORD=$(cat ~/.bluesky-app-password) npx tsx scheduled.ts >> bot.log 2>&1GitHub Actions で実行する
ボットのリポジトリに .github/workflows/post.yml を追加し、認証情報をリポジトリのシークレットとして保存します(Settings → Secrets and variables → Actions)。
name: Post
on:
schedule:
- cron: "*/15 * * * *"
workflow_dispatch:
jobs:
post:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npx tsx scheduled.ts
env:
BLUESKY_HANDLE: ${{ secrets.BLUESKY_HANDLE }}
BLUESKY_APP_PASSWORD: ${{ secrets.BLUESKY_APP_PASSWORD }}GitHub はスケジュールされたワークフローをベストエフォートで実行します。実行が遅れたり、負荷が高いときにはスキップされたりすることがあります。スクリプトの実行間隔はスケジュールと同じにしておき、正確な分単位の時刻には頼らないでください。
制限を守る
- 人間らしいペースで投稿しましょう。タイムラインを埋め尽くすボットは、ミュートや通報、レート制限の対象になります。
- サインインは投稿ごとではなく、実行ごとに 1 回にします。サインインには、より厳しい専用の制限があります。
- 活発なボットを動かす前に、Bluesky のレート制限 を読んでおきましょう。