今後はこっちを更新していく予定です
slackに毎日情報を流しているbotをcloudflare workersに移植してみる
前の記事で調べていた時にschedule triggerを使えば無料枠でもcron的な事ができそうだったので移植で素振りしてみる
cloudflare workersでslack botを動かすには@slack/bot
は動かないらしいので、以下の記事で紹介されているものを使う
各種slack用のトークンは上記の記事を参照
cron triggerについて
wrangler.yml
などにcron設定を書くとその設定に従って以下の用に定義した関数を実行してくれる
時間設定
wrangler.ymlに設定するのはこんな感じ(ドキュメントに書かれているサンプル)
[triggers] # Schedule cron triggers: # - At every 3rd minute # - At 3PM on first day of the month # - At 11:59PM on the last weekday of the month crons = [ "*/3 * * * *", "0 15 1 * *", "59 23 LW * *" ]
cronsに設定できる時間は3つまでらしい
実装
初期のテンプレートだとsrc/index.ts
にscheduled
関数を定義するとそれが実行される
export default { async scheduled(event: ScheduledEvent, env: SlackEdgeAppEnv, ctx: ExecutionContext): Promise<void> { // 処理 };
event
には実行された時間と発火したcronの設定値("*/3 * * * *"
のようなもの)が入っている。なのでevent.cron
の値を見て3つで出し分ける事も可能だと思う。
実際に作ってみる
実際に実行するときはcloudflareに環境変数として設定するがローカルでテストする時のために.dev.vars
にtoken設定などを書いておく
SLACK_BOT_TOKEN=xoxb-xxxxx SLACK_SIGNING_SECRET=xapp-xxxxx
scheduleのコードは以下。exec
関数でslackに投稿するデータの取得や組み立て、投稿までを行っている。
import { SlackApp, SlackEdgeAppEnv } from "slack-cloudflare-workers"; import * as xxx from "./features/xxx/index"; export default { async scheduled( _event: ScheduledEvent, env: SlackEdgeAppEnv, _ctx: ExecutionContext, ): Promise<void> { try { const app = new SlackApp({ env }); await xxx.exec(app); console.log("done"); } catch (e) { console.error(e); throw e; } }, }; // execの中のイメージ export function exec(app: SlackApp<SlackEdgeAppEnv>) { const data = await xxxApi(); const text = processing(data); await app.client.chat.postMessage({ channel: "random", text, });
これだけで簡単に投稿することができた
テスト
wrangler dev
を実行する際に--test-scheduled
というオプションを付けることでschedule発火用のエンドポイントが出てくる。
実行後以下のようにリクエストを投げることでscheduled関数を実行させることができる
curl "http://localhost:8787/__scheduled?cron=0+0+*+*+*"
疑問点
cron triggerで実行しconsole.log
でログを出すようにしてみたがどこで確認できるのかわからず、失敗した時のデバッグで困った。
またcronの設定のTZについてUTC固定?なので日本時間で実行する場合は+9時間後に実行される事に注意する必要がある。
cloudflare workerを始める
cloudflare workerずっと気になってたのでhelloworldとそのテストを触ってみたメモ
事前にアカウントは作っておいたのでプロジェクト作る所から。
プロジェクトを作る
pnpm create cloudflare@latest
これを実行するとcliでログインしていなければブラウザが起動して、cloudflareにログインする
コード
hello worldのテンプレートを選択して、生成されたsrc/index.ts
を確認すると以下のコードが生成されていた
export interface Env { } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { return new Response('Hello World!'); }, };
Response
をnewしてreturnするとレスポンスを返せるのがわかる
メモ
トリガー
実行方法としては他にも
- Alarm
- Queue
- Scheduled(cron的な)
- 3つまでの制限あり
- Tail
- リアルタイムでログを処理しロギングサービスや分析サービスに送信するために使うらしい
などトリガーにすることもできる*1
Node.js Compatibility
Cloudflare workerは完全にnodejsのAPIをサポートしてるわけではない
ただNode.js compatibilityというのがありwrangler.toml
にcompatibility_flags = [ "nodejs_compat" ]
と書いてフラグをオンにするとnode:buffer
など一部のAPIが使えるようになるらしい
環境変数
基本的にはwrangler.toml
の[vars]
部分に記述するらしいが、gitignoreの対象ではないので内容によっては書くのに抵抗がある
そういう時はローカルで動かす時は.dev.vars
というファイルを作り、その中に.env
のように記述するとwrangler dev
した時に引数のenv
に入る。
debug
デバッグはwrangler dev
で行う事ができる。実行するとローカルサーバーが起動する。
なのでdevを実行してるターミナルと、httpie*2を使ってリクエストを送るターミナルの2つを開いてやってみる。
# terminal A pnpm run dev # 裏でwrangler devを実行している # terminal B http localhost:8787 HTTP/1.1 200 OK Content-Length: 12 Content-Type: text/plain;charset=UTF-8 Hello World!
テスト
テストにはwrangler
パッケージから出てるunstable_dev
を使う
hello worldのテンプレートにはテストツールが入っていないのでvitestを入れた*3
公式のサンプルで示されてるのはjsなのでtsとして書いた
import {unstable_dev, type UnstableDevWorker} from 'wrangler' import {describe, beforeAll, afterAll, it, expect} from 'vitest' describe("Worker", () => { let worker: UnstableDevWorker beforeAll(async () => { worker = await unstable_dev('src/index.ts', { experimental: { disableExperimentalWarning: true } }) }) afterAll(async () => { await worker.stop() }) it('should return Hello World', async () => { const resp = await worker.fetch() if(resp) { const text = await resp.text() expect(text).toMatchInlineSnapshot(`"Hello World!"`) } }) })
今回は文字列を返すシンプルなものだが、KVなどを使い始めるとminiflareを使えばテストできるのか今後確認したい
その他
quickstartsとしてHonoを使っているテンプレートやGraphQL Yogaのテンプレートのようなものまであるようなのでgenerate
してみて中を見るのも良さそう。
ただwarngler generate
を実行すると
[WARNING] The
generate
command is no longer supported and will be removed in a future version.
と表示されるのでそのうち動かなくなりそう
またasync fetch
以外にもasync cron
のように書くと定期実行できるみたいなのでそっちもそのうち試してみる
*1:cloudflareのコンソール上にそれぞれTriggersとして定義されている
*3:ts-nodeとか使うとnodeのテストランナーでもできるとは思うTest runner | Node.js v21.2.0 Documentation