はじめに
Telegramボットを自作しようとして、公式のBot APIドキュメントを開いた瞬間に手が止まった、という方は多いのではないでしょうか。sendMessageやanswerCallbackQueryといったエンドポイントを一つずつHTTPリクエストで叩き、Webhookの検証やロングポーリングの再接続処理まで自前で書くのは、思った以上に骨が折れます。
Telegrafは、この面倒な部分をミドルウェアという一枚のレイヤーに集約してくれるNode.js製フレームワークです。bot.on('text', ...)のようにイベントごとにハンドラを登録するだけで、Bot APIの生の呼び出しを意識せずにボットを組み立てられます。TypeScriptの型定義も充実しているため、ctx(コンテキスト)オブジェクトの中身を推測でコーディングする必要もありません。
この記事では、Telegrafの基本的な使い方から、シーンによる会話フロー管理、インラインキーボード、Webhookデプロイまで、実際のボット開発で使う場面を想定して紹介します。
Telegrafとは
Telegrafは「Modern Telegram Bot Framework for Node.js」を掲げるオープンソースライブラリです。Telegram Bot APIのほぼ全機能をカバーしつつ、Express.jsライクなミドルウェアパターンでルーティングを記述できるのが最大の特徴です。AWS Lambda、Firebase Functions、Fly.ioなどサーバーレス環境へのデプロイ実績も豊富で、個人のちょっとしたボットから商用サービスまで幅広く使われています。
主な特徴
- ミドルウェアベースの設計 -
bot.use()でExpressのようにハンドラを連結でき、ロギングや認証などの共通処理を挟み込める - Scenes(シーン)による会話管理 - 複数ステップの対話フローを、状態遷移として宣言的に書ける
- 充実したTypeScript型定義 -
ctx.messageやctx.callbackQueryの型がAPIの種類ごとに絞り込まれる - 柔軟なWebhook対応 - http/https標準モジュールに加え、ExpressやFastifyのミドルウェアとしても組み込める
- 軽量 - 依存が少なく、サーバーレス関数のコールドスタートにも影響しにくい
インストール
npm、yarn、pnpmのいずれでも導入できます。
npm install telegraf
yarn add telegraf
pnpm add telegraf
Node.js 18以上が推奨環境です。TypeScriptで使う場合も型定義が同梱されているため、追加で@types/telegrafを入れる必要はありません。
基本的な使い方
まずはBotFatherから取得したトークンを使って、最小構成のボットを起動してみます。Telegrafのインスタンスにstart(/startコマンド)やtext(テキストメッセージ)といったイベントハンドラを登録し、最後にlaunch()でロングポーリングを開始します。
const { Telegraf } = require('telegraf')
const bot = new Telegraf(process.env.BOT_TOKEN)
bot.start((ctx) => ctx.reply('こんにちは!何かお手伝いできることはありますか?'))
bot.help((ctx) => ctx.reply('/startで会話を始められます'))
bot.on('text', (ctx) => {
ctx.reply(`「${ctx.message.text}」を受け取りました`)
})
bot.on('sticker', (ctx) => ctx.reply('👍'))
bot.launch()
// Ctrl+CやプロセスKillに対して安全に停止する
process.once('SIGINT', () => bot.stop('SIGINT'))
process.once('SIGTERM', () => bot.stop('SIGTERM'))
ctxには受信したメッセージの情報がすべて詰まっており、ctx.reply()を呼ぶだけで送信元のチャットに返信できます。ミドルウェアを追加したいときは、bot.use()にコールバックを渡すだけです。
bot.use((ctx, next) => {
console.log(`${ctx.updateType}を受信しました`)
return next() // 次のミドルウェア・ハンドラに処理を渡す
})
このパターンはExpressのミドルウェアとほぼ同じ感覚で書けるため、認証チェックやレート制限といった横断的な処理を挟みやすいのがTelegrafの強みです。
実践的なユースケース
Telegrafは単純な一問一答ボットだけでなく、複数ステップの対話やボタン操作、本番運用でのWebhook配信まで対応できます。ここでは代表的な3つのパターンを紹介します。
インラインキーボードでのボタン操作
テキスト入力の代わりにボタンで選択させたい場合は、Markup.inlineKeyboard()を使います。ユーザーがボタンを押すとbot.action()で登録したコールバックが呼ばれ、ctx.answerCbQuery()でTelegram側のローディング表示を止めつつctx.editMessageText()でメッセージを書き換えられます。
const { Telegraf, Markup } = require('telegraf')
const bot = new Telegraf(process.env.BOT_TOKEN)
bot.command('menu', (ctx) => {
return ctx.reply(
'メニューを選んでください',
Markup.inlineKeyboard([
Markup.button.callback('注文する', 'order'),
Markup.button.callback('問い合わせ', 'inquiry'),
])
)
})
bot.action('order', async (ctx) => {
await ctx.answerCbQuery()
return ctx.editMessageText('ご注文ありがとうございます!')
})
bot.action('inquiry', async (ctx) => {
await ctx.answerCbQuery()
return ctx.editMessageText('お問い合わせ内容をどうぞ')
})
bot.launch()
bot.action()に渡す文字列は、ボタン側のcallback_dataと一致させる必要があります。ボタンを増やすたびに新しいactionハンドラを足していくだけなので、メニューの階層化も容易です。
Scenesによる複数ステップの会話フロー
名前や日付など、複数の質問を順番に投げかけて回答を集めたい場合はScenesが便利です。1つのシーンが1つの対話フェーズに対応し、ctx.scene.enter()で開始、ctx.scene.leave()で終了します。
const { Telegraf, Scenes, session } = require('telegraf')
const nameScene = new Scenes.BaseScene('name')
nameScene.enter((ctx) => ctx.reply('お名前を教えてください'))
nameScene.on('text', (ctx) => {
ctx.session.name = ctx.message.text
ctx.reply(`${ctx.session.name}さん、登録ありがとうございます!`)
return ctx.scene.leave()
})
const stage = new Scenes.Stage([nameScene])
const bot = new Telegraf(process.env.BOT_TOKEN)
bot.use(session())
bot.use(stage.middleware())
bot.command('register', (ctx) => ctx.scene.enter('name'))
bot.launch()
session()ミドルウェアがユーザーごとの状態(ctx.session)を保持してくれるため、シーンをまたいで入力内容を引き継げます。質問数が増えても、シーンを追加してStageの配列に足すだけで会話フローを拡張できます。
Webhookでの本番デプロイ
開発中はロングポーリング(bot.launch())で十分ですが、サーバーレス環境や常時稼働のWebサーバーではWebhook配信に切り替えるのが一般的です。TelegrafはExpressのミドルウェアとしてcreateWebhook()を組み込めます。
const express = require('express')
const { Telegraf } = require('telegraf')
const bot = new Telegraf(process.env.BOT_TOKEN)
bot.start((ctx) => ctx.reply('Webhook経由で起動しました'))
const app = express()
app.use(await bot.createWebhook({ domain: process.env.DOMAIN }))
app.listen(3000, () => console.log('Webhookサーバーを起動しました'))
createWebhook()はTelegram側へのWebhook登録も自動で行うため、setWebhookを個別に呼び出す手間がありません。ロングポーリングからの切り替えも、bot.launch()をこのミドルウェア組み込みに差し替えるだけで完結します。
まとめ
Telegrafは、Telegram Bot APIの生のHTTP呼び出しをミドルウェアという扱いやすい形に抽象化してくれるフレームワークです。基本的なイベントハンドリングから始め、インラインキーボードでボタン操作を実装し、Scenesで複雑な対話フローを組み、最終的にWebhookで本番運用に載せる、という流れが一貫したAPIで書けるのが魅力です。
まずはbot.start()とbot.on('text', ...)だけの最小構成から始めて、必要になった機能を少しずつ足していくのがおすすめです。
