👅

slackで自分のアカウントからメッセージを送るapiをhonoで作った話

に公開

こんにちは、あやにゃんです!

友達の研究室が毎日平日の朝におはようございますとスラックに送らなければならないと聞いて、それ、自動化しちゃおうよ!!となったので作りました!

ぜひ同じ境遇の人は参考にしてください!

最近、「Slackに自分のアカウントから、毎朝9時台にランダムな時間でメッセージを投稿するAPI」を作りました。

しかも、

  • Hono

  • Cloudflare Workers

  • TypeScript

    を組み合わせて、めちゃくちゃ軽くてスマートに動いてます(3150)

この記事ではその構成・コード・ちょっとハマったところなどをまとめました。


🛠️ 作ったもの

  • 平日のみ
  • 朝9:00〜9:59の間にランダムな分にSlackへ「おはようございます ☀️」を
  • Botではなく、自分のSlackアカウントで投稿する!

🧱 技術スタック

技術 用途
Hono 軽量なWebフレームワーク
Cloudflare Workers サーバレスでAPIホスティング
Slack API (chat.postMessage) 自分のユーザーとして投稿
Workers KV 投稿予定時間の一時保存

🌅 構成の概要

1日2回 API が呼ばれます:

  1. 深夜0時:ランダムな分数(0〜59)を生成して保存
  2. 朝9時台(毎分):現在の分が一致したら投稿!

Cloudflare Workersの Cron Triggers 機能を使って、スケジュール実行しています。

なぜCron Triggersを使用したかと申しますと、
Cloudflare Workers の無料プランでは、1リクエストあたりの最大実行時間が 30秒 までです 😢
なので、ランダムな数を生成して待つよりも、深夜にランダムな数を生成して予約する方がいいのです!!


🧑‍💻 コード:Hono + Workers

Hono CLI でプロジェクトを作成

bun create hono@latest slack-hono-bot
  • プロンプトで選ぶ項目
    1. Target directory: slack-hono-bot
    2. Template: cloudflare-workers
    3. Package manager: bun(またはお好みで npm/yarn/pnpm

デフォルトのディレクトリ構成を確認
生成された直後の構成はこんな感じです:

slack-hono-bot/
├── package.json
├── public/
│   ├── favicon.ico
│   └── static/…
├── src/
│   └── index.ts        ← ここに API ロジックを書く
└── wrangler.toml       ← Cloudflare Workers の設定!なければ自分で作る

Hono

index.ts
/// <reference types="@cloudflare/workers-types" />
import { Hono } from 'hono'

type Env = {
  SLACK_USER_TOKEN: string
  SLACK_CHANNEL_ID: string
  POST_TIME: KVNamespace
}

const app = new Hono<{ Bindings: Env }>()

// 深夜0時:投稿分数をランダムに設定
app.get('/set-time', async (c) => {
  const randomMinute = Math.floor(Math.random() * 60)
  await c.env.POST_TIME.put('next_post_minute', String(randomMinute))
  return c.text(`明日の投稿時間は 9:${String(randomMinute).padStart(2, '0')} に設定しました`)
})

// 朝9時台:もし一致したらSlackに投稿
app.get('/post-if-match', async (c) => {
  const now = new Date()
  const weekday = now.getDay() // 月〜金:1〜5
  const currentMinute = now.getMinutes()

  if (weekday < 1 || weekday > 5) {
    return c.text('今日は平日ではありません。')
  }

  const savedMinuteStr = await c.env.POST_TIME.get('next_post_minute')
  const savedMinute = savedMinuteStr ? parseInt(savedMinuteStr, 10) : -1

  if (currentMinute === savedMinute) {
    const token = c.env.SLACK_USER_TOKEN
    const channelId = c.env.SLACK_CHANNEL_ID

    const payload = {
      channel: channelId,
      text: 'おはようございます ☀️(ランダム投稿)',
    }

    const res = await fetch('https://slack.com/api/chat.postMessage', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(payload),
    })

    const data: { ok: boolean; error?: string } = await res.json()

    if (!data.ok) {
      return c.text(`Slack投稿失敗: ${data.error}`, 500)
    }

    return c.text(`✅ 投稿しました!(9:${currentMinute}`)
  }

  return c.text(`今は ${currentMinute}分、投稿予定は ${savedMinute}`)
})

export default app


🔐 環境変数の設定(Wrangler)

環境変数の取得はこちら💁🏻‍♀️ https://api.slack.com/apps
SLACK_USER_TOKEN
https://zenn.dev/axt/articles/932dd0ff535a31
SLACK_CHANNEL_ID
https://qiita.com/YumaInaura/items/0c4f4adb33eb21032c08

npx wrangler secret put SLACK_USER_TOKEN      # xoxp-... 自分のSlackアカウントのOAuthトークン
npx wrangler secret put SLACK_CHANNEL_ID      # 投稿先のチャンネルID


🗂 wrangler.toml

次に、
Cloudflare Workers の KVストア(永続的なキー・バリュー保存) を使用します

npx wrangler kv namespace create "POST_TIME"

{
  "kv_namespaces": [
    {
      "binding": "POST_TIME",
      "id": "xxxxx..." # 自動で生成される
    }
  ]
}

が出てくるのでこれらをwrangler.toml以下のような形で入れます。
wrangler.tomlがなければ自分で作ってください。

wrangler.toml
コピーする編集する
name = "slack-hono-bot"
compatibility_date = "2025-04-17"

[[kv_namespaces]]
binding = "POST_TIME"
id = "xxxxx..." # 自動で生成される

[build]
command = "bun run build"

[build.upload]
main = "dist/index.js"
format = "modules"

[triggers]
crons = [
  "0 15 * * *",        # JST 0:00 → UTC 15:00(set-time)
  "0-59 0 * * 1-5"     # JST 9:00〜9:59 → UTC 0:00〜0:59(post-if-match)
]

さらに、wrangler.jsoncに

"kv_namespaces": [
    {
      "binding": "POST_TIME",
      "id": "xxxxx..." # 自動で生成される
    }
  ]

を追加し、

bun run build

でビルドします


🧠 ハマったポイント

  • KVNamespace の型定義がない → @cloudflare/workers-types を入れる
  • String.padStart() が使えない → tsconfig.jsonlib"ES2020" を追加
  • fetch().json() の型が unknown → 明示的に型定義する

🎁 補足:なぜ「自分のアカウント」で投稿?

SlackのIncoming Webhookだと「Bot名」でしか投稿できませんが、自分のアカウントから送ると自然で教授もニッコリ‼️


🙏 最後に

ここまで読んでくれてありがとうございます!

Cloudflare Workers と Hono の組み合わせは、無料枠が充実していてさらにデプロイが簡単なので開発体験がめちゃ良い〜‼️

「Slack自動投稿してみたい」という方の参考になれば嬉しいです ☀️

Discussion