Telegramボットの構築とデプロイ方法は?
この記事では、Node.jsとBack4appのbackend-as-a-serviceを使ってTelegramボットを構築します。そして、Back4appのコンテナを使ってデプロイします。
ボットとは、ネットワーク上やプラットフォーム上で自動化された反復タスクを実行できるソフトウェアのことです。Telegram ボットは Telegram ボット API を使用して作成され、Telegram で動作するように特別に設計されています。
Telegramボットをデザインして、Telegram上で様々なタスクを実行させることができる。例えば、天気予報やニュースのヘッドラインなどの情報を取得したり、娯楽のためのクイズを主催したりすることができる。
また、リマインダーのスケジューリングや簡単なユーザー認証などのタスクを自動化することもできる。
Contents
開発環境のセットアップ
この記事では、ユーザーが好きな都市の天気を6時間ごとに自動更新したり、選択した都市の天気情報を即座に入手したりできるTelegramボットを紹介する。
プロジェクト・ディレクトリを設定するには、以下のコマンドを実行する:
# プロジェクトディレクトリを作成する
mkdir telegram-weather-bot
# プロジェクトディレクトリにcdする
cd telegram-weather-bot
# npmを初期化する
npm init -y
上記のコマンドは、telegram-weather-bot
ディレクトリを作成し、その中に移動し、その中でnpmを初期化する。
package.json
ファイルに、ボットを実行するための以下のスタートスクリプトを追加します:
"start": "node server.js"
次に、以下のコマンドを実行して、このプロジェクトに必要なパッケージをインストールする:
npm install axios node-telegram-bot-api dotenv node-schedule parse
上記のコマンドは以下のパッケージをインストールした:
- axiosです:このライブラリを使って天気APIにHTTPリクエストを行う。
- node-telegram-bot-api:このライブラリは Telegram Bot API とのやりとりを簡略化します。
- dotenv: このパッケージは.envファイルから環境変数をロードする手助けを します。
- node-schedule:タスクをスケジュールし、天気の更新を自動化するにはこのパッケージが必要です。
- parse:このパッケージはBack4app Parse Backendとやり取りするために必要です。
これで開発環境のセットアップは完了です。次に、Telegramでボット・アカウントを作成する必要がある。
Telegram Botアカウントの作成
Telegramボットアカウントは、Telegramプラットフォーム上でのボットのアイデンティティとして機能します。ユーザーはこのアカウントを通じてボットとやりとりすることができます。
Telegramボットを作成するには、Telegramアプリを起動し、検索バーに「BotFather」と入力し、検証済みのトップ結果をクリックします。
BotFatherは、他のボットを素早く開発できるTelegramボットです。コマンド/newbotを入力して、ボットの作成プロセスを開始します。ボットの名前と固有のユーザー名を入力します。
ボットの名前とユーザー名を入力すると、BotFather がユニークなトークンを提供し、ボットのアカウントとのやり取りや管理ができるようになります。
ルートディレクトリに.env
ファイルを作成し、そのファイルにボットトークンを貼り付けて、ボットトークンをプロジェクトに保存します。このように:
TELEGRAM_BOT_TOKEN = <YOUR_BOT_TOKEN>
ボットトークンを取得し、プロジェクトに保存したら、Telegramボットを構築できます。
Telegram ボット構築
このセクションでは、Telegram天気ボットを作成し、コマンドの処理、ユーザーの状態の管理、天気APIからのデータの取得などの機能を実行するためにインストールしたライブラリを統合する手順を説明します。
ボットのディレクトリを以下のように構成し、ビルド・プロセスを開始します:
telegram-weather-bot/
├── node_modules/
├── src/
| ├── bot.js # ボットとユーザーのやり取りを処理するためのファイル
| ├── weather.js # 気象データを取得するためのモジュール
| ├── stateManager.js # Back4Appでユーザー状態を管理するためのモジュール
| └── cityManager.js # ユーザーの都市設定を管理し、天気予報を送信します
├── .env
├── package.json
├── server.js # アプリケーションのエントリポイント
└── package-lock.json
気象情報の入手
気象情報を取得するには、OpenWeatherMap APIを使用します。
このAPIへのリクエストにはAPIキーが必要です。このAPIキーを取得するには、アカウントにログインしてください(アカウントをお持ちでない場合は作成してください)。
次に、プロフィールの「My API Keys」セクションに移動し、API Keyをコピーします。
OpenWeatherMap の API キーを.env
ファイルに保存します:
OPENWEATHERMAP_TOKEN = <YOUR_OPEN_WEATHER_MAP_APIKEY>
ボットの機能は天気情報の取得が中心なので、OpenWeatherMap の API に GET リクエストを行い、axios.get
メソッドで天気情報を取得する関数を定義します。
以下のコードブロックをweather.js
ファイルに追加して、OpenWeatherMap APIから天気情報のGETリクエストを行います:
const axios = require('axios');
const apiKey = process.env.OPENWEATHERMAP_TOKEN;
async function getWeather(city) {
try {
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`;
const response = await axios.get(url);
const temp = response.data.main.temp;
const description = response.data.weather[0].description;
return `The weather in ${city} is ${temp}°C with ${description}.`;
} catch (error) {
console.error('Error fetching weather:', error);
throw error;
}
}
module.exports = { getWeather }
上記のコード・ブロックは、都市
名を引数にとり、APIのレスポンスから気温と天気の説明を返すgetWeather
関数を定義している。
ユーザーに6時間ごとの定期的な天気更新を提供するためには、getWeather
関数を実行して希望する都市の天気更新を返すスケジューラー関数を実装する必要があります。
ウェザー・アップデートのスケジューリング
希望する都市の天気の更新を返すジョブをスケジュールするには、cityManager.jsに
以下のコードブロックを追加します:
const schedule = require('node-schedule');
const weather = require('./weather');
let userCities = {};
function setCity(chatId, city) {
userCities[chatId] = city;
}
function init(bot) {
schedule.scheduleJob('0 */6 * * *', function() {
for (let chatId in userCities) {
const city = userCities[chatId];
weather.getWeather(city).then(response => {
bot.sendMessage(chatId, response);
}).catch(error => {
bot.sendMessage(chatId, "Failed to retrieve weather.");
});
}
});
}
module.exports = { setCity, init };
上のコード・ブロックは、setCityと
initという
2つの関数を作っている。
このコード・ブロックは、ボット・ユーザーのTelegramチャットIDをsetCity
関数を使って都市名にマッピングすることで、ボット・ユーザーが興味を持っている都市を追跡するためのuserCities
オブジェクトを宣言している。
この機能により、ボットはユーザーの好みに合わせて、どの都市の天気予報を受信するかを設定することができます。
コードブロックのinit
関数は、userCitiesに
格納されているすべてのチャットIDを反復処理する6時間ごとのスケジュールされたタスクを設定します。init
関数はweather
モジュールを使用して、その都市の現在の天気を取得します。
あなたのボットは、異なるユーザーとその好みの都市を追跡するために、ユーザーの状態を永続化する必要があります。これは、Back4appのバックエンドをサービスとして利用することで実現できます。
ボットが様々なユーザーとその好みの都市を追跡できるようにするには、Back4appのバックエンドをサービスとして使用して、ボットユーザーの好みの都市を保存します。
Back4appバックエンドの作成
Back4appのバックエンドをサービスとして利用するには、Back4appのアカウントが必要です。お持ちでない場合は、無料でサインアップできます。
アカウントにログインし、右上の「NEW APP」ボタンをクリックします。アプリケーションに名前を付け、「作成」ボタンをクリックします。
CREATE “ボタンをクリックすると、Back4appがアプリケーションを生成し、アプリケーションのダッシュボードに誘導します。
Node.jsプロジェクトからBack4Appのアプリインスタンスに接続するには、インストールしたparse SDKをBack4Appのアプリケーション認証情報で初期化する必要があります:アプリケーション
IDとJavascript KEY
です。
サイドバーの “App Settings “をクリックして “Security & Keys “セクションに移動し、Back4appからアプリケーション
IDとJavaScript Keyを
入手してください。
.env
ファイルに保存してください。現在の.env
ファイルは以下のようなものになっているはずです:
TELEGRAM_BOT_TOKEN= "<YOUR_TELEGRAM_BOT_TOKEN_HERE>"
OPENWEATHERMAP_TOKEN= "<YOUR_OPENWEATHERMAP_TOKEN_HERE>"
BACK4APP_APP_ID= "<YOUR_BACK4APP_APP_ID_HERE>"
BACK4APP_JAVASCRIPT_KEY= "<YOUR_BACK4APP_JAVASCRIPT_KEY_HERE>"
次に、userId(文字列)とstate(オブジェクト)の2つのフィールドを持つ新しい “UserState “クラスを作成する必要があります。このタスクは、Back4appのAIエージェントを使用して行います。
それぞれのフィールドを持つこのクラスを作成するには、画面の「AIエージェント」タブに移動し、エージェントに以下のプロンプトを与えます:
Create a new class, "UserState," in my Back4app application with the APP ID “<YOUR_BACK4APP_APP_ID_HERE>”. The "UserState" class will have two fields: userId (string) and state (object).
下の画像のような反応が返ってくるはずだ:
Back4appのダッシュボードを確認すると、UserState
クラスが正常に作成されているはずです。
Telegramボットでユーザーの状態を管理する
ボットのユーザーとボットの間のインタラクションの流れを管理するには、ボットがユーザーからどのようなコマンドを期待するかを示すユーザーステートを定義する必要があります。
start
コマンドを除けば、ボットは主に2つのコマンドを理解します。各ユーザとの対話(コマンドを含む)は、UserState
クラスにチャットIDとともに格納されているユーザの状態を更新するトリガーとなります。
この状態管理によって、ボットは会話の中で各ユーザーの特定の詳細を記憶することができる。
Back4Appデータベースに作成したUserState
クラスでユーザのステータスのトラッキングを開始するには、Parse SDKを認証情報で初期化する必要があります。
以下のコードブロックをstateManager.jsに
追加し、Back4app Parse Backendに接続するために必要な認証情報でParse SDKを初期化します:
// stateManager.js
const Parse = require('parse/node');
Parse.initialize(
process.env.BACK4APP_APP_ID,
process.env.BACK4APP_JAVASCRIPT_KEY
);
Parse.serverURL = '<https://parseapi.back4app.com/>';
次に、stateManager.js
ファイルで、Back4app ParseバックエンドのUserState
クラスのスキーマを表すParseオブジェクトを定義し、ボットがデータベースに保存されたユーザーのステートを操作できるようにします。
こんな感じだ:
// stateManager.js
const UserState = Parse.Object.extend('UserState');
次に、データベースに保存されているユーザーIDに基づいて、ボットが特定のユーザーの現在の会話状態を取得できるようにする必要があります。
ユーザの状態を取得するには、UserState
クラスにユーザのIDを指定してクエリを実行し、データベースに保存されている状態を見つける必要があります。
こんな感じだ:
// stateManager.js
async function getUserState(userId) {
const query = new Parse.Query(UserState);
query.equalTo('userId', userId.toString());
const userState = await query.first();
return userState ? userState.get('state') : null;
}
上記のコードブロックは、非同期にユーザー状態データをフェッチするgetUserState
関数を定義している。
この関数は、引数(userId
)に基づいてユーザの状態オブジェクトを検索するクエリを作成し、見つかった場合はユーザの状態を取得します。
ユーザーの状態を取得した後、ボットがユーザー
IDと提供された状態
情報に基づいて、そのユーザーに関連する会話状態を更新できるようにする必要があります。
例えば、会話の状態を更新することで、ボットが希望する都市を更新し、定期的な天気予報を取得できるようになる。
こんな感じだ:
// stateManager.js
async function setUserState(userId, state) {
const query = new Parse.Query(UserState);
query.equalTo('userId', userId.toString());
let userState = await query.first();
if (!userState) {
userState = new UserState();
userState.set('userId', userId.toString());
}
userState.set('state', state);
await userState.save();
}
上記のコードブロックの関数は、userIdに基づいて
ユーザー状態オブジェクトを検索するクエリを構築し、存在しない場合は新しいUserState
オブジェクトを作成し、state
属性を設定し、オブジェクトをデータベースに保存します。
次に、以下のコード・ブロックをstateManager.js
ファイルに追加して、ユーザーの状態をリセットする関数を定義する:
// stateManager.js
async function resetUserState(userId) {
await setUserState(userId, {});
}
resetUserState
関数は、非同期にユーザの状態を空のオブジェクト{}に
設定し、データベース内のユーザの状態をリセットします。
ステートオブジェクトが常に存在するようにして、以前と新しいボットユーザーのアクションを追跡します。以下のコードブロックをstateManager.js
ファイルに追加します:
// stateManager.js
async function ensureUserState(userId) {
let state = await getUserState(userId);
if (!state) {
await setUserState(userId, {});
}
}
このコードブロックは、ユーザーIDに基づいてステートオブジェクトを取得するsecureUserState
関数を定義しており、Back4app ParseバックエンドのUserState
クラスに指定されたuserIdの
ユーザーステートデータが存在することを確認します。
getUserState
関数でユーザーの状態を取得し、状態が存在しない場合は、setUserState
関数で空のオブジェクト{}を
設定します。
すべての関数をエクスポートして、他のJavascriptソースファイルで利用できるようにします:
// stateManager.js
module.exports = {
getUserState,
setUserState,
resetUserState,
ensureUserState,
};
ユーザーとボットのやり取り
ボットがstart
、getWeather
、setCity
コマンドをリッスンできるようにするには、3つのコマンドにテキストをマッチさせてコールバック関数を実行するイベントリスナーを定義します。
これにより、ボットはコマンドに関連するタスクを実行できるようになる。
Telegram Bot APIとやりとりしてボットの動作を定義するには、node-telegram-bot-api
ライブラリからTelegramBot
クラスをインポートし、クラスの新しいインスタンスを作成します:
// bot.js
const TelegramBot = require('node-telegram-bot-api');
const token = process.env.TELEGRAM_BOT_TOKEN;
const bot = new TelegramBot(token, { polling: true });
上のコード・ブロックは、ボット・トークンとオブジェクトの2つの引数でTelegramBot
クラスの新しいインスタンスを生成します。
このオブジェクトは、ボットがTelegramサーバーからのメッセージやアップデートを継続的にチェックできるように、ポーリング値をtrueに
設定します。
次に、このプロジェクトで作成したモジュールをインポートします。これらのモジュールを使って、天気予報の更新を取得したり、希望する都市を設定したり、ユーザーの状態を管理したりといったボットの機能を実装します。
以下のコードを追加して、必要なモジュールをインポートする:
// bot.js
const weather = require('./weather');
const cityManager = require('./cityManager');
const stateManager = require('./stateManager');
上記のコードブロックは、すべてのソースファイルで作成した関数をインポートし、bot.js
ファイルでこれらの関数を呼び出せるようにします。
コマンドをセットアップするには、TelegramBot
クラスで利用可能なonText
メソッドを使用します。このメソッドはリスナーを設定し、コールバック関数を実装してロジックを実行します。
以下のコードブロックをbot.js
ファイルに追加して、ボットがonText
メソッドで積極的にリッスンする/start
コマンドハンドラを設定します:
// bot.js
bot.onText(/\\/start/, async (msg) => {
const welcomeMessage = "Welcome to the Weather Bot! Use the commands below to interact:\\n" +
"/setCity - Set your preferred city for weather updates.\\n" +
"/getWeather - Get instant weather information for any city.\\n";
await bot.sendMessage(msg.chat.id, welcomeMessage);
stateManager.ensureUserState(msg.chat.id);
});
上のコードブロックは、ボットがコールバック関数を実行し、ボットの動作について詳しく説明するウェルカムメッセージをユーザーに送信するようにします。
ボットはsecureUserState
関数でユーザーの初期状態を設定し、各ユーザーとのインタラクションでクリーンな状態を確保します。
次に、以下のコードブロックをbot.js
ファイルに追加して、ボットがonText
メソッドで積極的にリッスンする/setCity
コマンドハンドラを設定します:
// bot.js
bot.onText(/\\/setCity$/, async (msg) => {
stateManager.setUserState(msg.chat.id, { expect: 'SET_CITY' });
bot.sendMessage(msg.chat.id, "Which city do you want to set as your preferred city for weather updates?");
});
上のコード・ブロックでは、「/setCity
」コマンドで、天気予報の更新に使用する都市を指定できる。
このコマンドを受信すると、ボットはsetUserState
関数を使用してユーザーの状態を「SET_CITY」に更新し、ユーザーが希望する都市を入力するよう促します。
以下のコードブロックをbot.js
ファイルに追加して、ボットがonText
メソッドで積極的にリッスンする/getWeather
コマンドハンドラを設定します:
// bot.js
bot.onText(/\\/getWeather$/, async (msg) => {
stateManager.setUserState(msg.chat.id, { expect: 'GET_WEATHER' });
bot.sendMessage(msg.chat.id, "Which city do you want to get weather information for?");
});
getWeather
“コマンドは、ボットがユーザーの状態を “GET_WEATHER “に更新するトリガーとなり、即座に天気情報が欲しい都市を入力するよう促す。
着信メッセージの処理
Telegram ボットの受信メッセージを処理するには、ボットが新しいメッセージを受信するたびにコールバック関数をトリガーするイベントリスナーをセットアップする必要があります。
以下のコードブロックをbot.js
ファイルに追加して、コマンドとユーザーの応答を聞くためのメッセージハンドラを設定します:
// bot.js
// Handle incoming messages
bot.on('message', async (msg) => {
if (msg.text.startsWith('/')) {
// If the message is a command, reset the user state
stateManager.resetUserState(msg.chat.id);
} else {
// If it's not a command, check user state
const state = await stateManager.getUserState(msg.chat.id);
if (state && state.expect === 'SET_CITY') {
// If expecting SET_CITY, set city and reset state
const city = msg.text;
cityManager.setCity(msg.chat.id, city);
bot.sendMessage(msg.chat.id, `City set to ${city}. You will receive weather updates every 2 minutes.`);
stateManager.resetUserState(msg.chat.id);
} else if (state && state.expect === 'GET_WEATHER') {
// If expecting GET_WEATHER, get weather and reset state
const city = msg.text;
weather.getWeather(city).then(response => {
bot.sendMessage(msg.chat.id, response);
}).catch(error => {
bot.sendMessage(msg.chat.id, "Failed to retrieve weather.");
});
stateManager.resetUserState(msg.chat.id);
}
}
});
// Initialize the init function from cityManager.js for regular weather updates
cityManager.init(bot);
このコード・ブロックは、送られてきたテキスト・メッセージをリッスンする一般的なメッセージ・ハンドラをセットアップし、インタラクション・ロジックの中核として機能する。
メッセージがフォワード・スラッシュ(“/”)で始まる場合、それはコマンドとみなされる。この場合、ハンドラはユーザーの状態をリセットし、新しいコマンドのために再出発するようにします。
そうでない場合、ハンドラはユーザーの現在の状態(stateManager.getUserState
)をチェックし、ユーザーの意図を理解する。
ユーザーが”/setCity “コマンドを渡すと、ボットハンドラーはcityManager.setCityを
使用して選択した都市を保存し、更新を確認する。
ユーザーが”/getWeather
“コマンドを渡した場合、ハンドラーはweather.getWeather
関数を使用して指定された都市の天気情報を取得し、その応答をユーザーに送り返す。
上記のコードブロックは、次にcityManager.init(bot)
を実行し、希望する都市の天気の更新を取得するスケジュールされたタスクを実行する。
ボットのロジック処理が完了したら、Back4appコンテナにデプロイする際に実行できるようにサーバーをセットアップします。
エントリーファイルの作成
ボットアプリケーション全体を初期化するエントリーファイルを作成します。このファイルは2つの機能を果たします。
- エントリーファイルは、ボットがTelegramのAPIとどのようにやりとりし、ユーザーメッセージに応答するかを処理するJavaScriptファイルを要求することで、ボットのロジックを実行します。
- エントリーファイルは、Back4appコンテナ上でボットを動作させるためのサーバーもセットアップします。Back4appコンテナは、アプリケーションのデプロイを成功させるために公開ポートを必要とします。
プロジェクト・ディレクトリ内のserver.js
ファイルに移動する。このファイルがサーバーの作成を処理します。以下のコード・ブロックをserver.js
ファイルに追加します:
const http = require('http');
require('./src/bot'); // Import bot logic
const port = 3000;
http
.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('My Telegram bot is running\\n');
})
.listen(port, () => {
console.log(`Server running on port ${port}`);
});
上のコード・ブロックは、シンプルなHTTPサーバーを作成することで、Telegramボットの実行をセットアップしている。このサーバーはエントリー・ポイントとして機能し、ボットをコンテナの環境内で機能させる。
スクリプトはボットのコアロジックをインポートし、Back4appのポートを指定することで、ボットをBack4appコンテナ上にデプロイできるようにします。
エントリーファイルを作成したら、Back4appコンテナにデプロイする前にローカルでTelegramボットをテストすることができます。
Telegramボットのテスト
以下のコマンドを実行してTelegramボットを起動します:
node server.js
ボットと対話するには、Telegramアプリを開き、検索バーでボットの名前を検索します。ボットとのチャットに入り、/startコマンドを送信します。
ボットは歓迎メッセージとコマンドのリストで応答するはずです。ボットにコマンドを送る。
このボットでは、/setCity
コマンドで好みの都市を設定し、定期的に天気予報の更新を受け取ることができる。
上の画像にあるように、/getWeather
コマンドは、特定の都市の天気情報を即座に取得することができる。
ボットは上の画像と同じように動作し、あなたのコマンドに反応し、意図したとおりにあなたと対話するはずです。
Telegram BotをBack4Appコンテナにデプロイする
ボットをテストし、正しく動作することを確認したら、Back4appコンテナにデプロイすることができます。そのためには、まずプロジェクト用のDockerfileを
作成する必要があります。
プロジェクトのルート・ディレクトリにDockerfile
ファイルを作成し、以下のコード・ブロックを追加する:
# Specify base image
FROM node:18-alpine
# Specify the working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
# Run the app
CMD ["npm", "start"]
Dockerfileを
作成したら、ローカルリポジトリをGitHubにプッシュし、Back4appコンテナにデプロイできるようにします。
次に、GitHubアカウントにBack4App Containers GitHub Appをインストールし、アプリケーションのコードリポジトリにアクセスするために必要なパーミッションを与えます。
GitHubの設定が完了したら、Back4appのホームページに移動し、画面右上の “New App“ボタンをクリックします。
初期化画面が表示されますので、作成したいアプリの種類を選択してください。下の画像のように、Back4appエージェントのオプションを選択します。
Back4app Agentオプションを選択すると、Back4app AIエージェントのページにリダイレクトされます。
以下のプロンプトを入力し、アプリケーションをBack4appコンテナにデプロイします:
Deploy my "YOUR_REPOSITORY_URL" repository on GitHub to Back4App Containers.
Here are the required environmental variables:
TELEGRAM_BOT_TOKEN = "TELEGRAM_BOT_TOKEN"
OPENWEATHERMAP_TOKEN = "WEATHER_API_TOKEN"
BACK4APP_APP_ID = "BACK4APP_APP_ID"
BACK4APP_JAVASCRIPT_KEY = "BACK4APP_JAVASCRIPT_KEY"
プレースホルダを実際の値に置き換えます。上記のプロンプトが、デプロイメントプロセスを開始します。完了すると、AIエージェントが応答し、デプロイの成功または保留を示します。
以下のような返事が返ってくるはずだ:
保留中のデプロイが表示されたら、Back4app コンテナダッシュボードでアプリのデプロイ状況を監視できます。あるいは、Back4app コンテナ上に手動でアプリケーションをデプロイすることもできます。
結論
この記事では、Node.jsを使ってTelegramボットを作成する方法を学びました。Telegram ボットアカウントの作成方法、ボットコマンドの処理方法、ユーザーインタラクションの処理方法について説明しました。
また、Back4appのAIエージェントの助けを借りてボットのバックエンドを作成し、ユーザーの状態を効率的に管理したり、定期的に天気の更新を受け取るためにユーザーが希望する都市などの情報を会話に保存できるようにしました。
完全なコードはGitHubリポジトリにある。