如何构建和部署 Telegram 机器人?

在本文中,您将使用 Node.js 和 Back4app 的后台即服务(backend-as-a-service)构建一个 Telegram 机器人。然后,您将使用 Back4app 容器进行部署。

机器人是一种软件,可以在网络或平台上自动执行重复性任务。Telegram 机器人是使用 Telegram 机器人 API 创建的,专门设计用于在 Telegram 上运行。

您可以设计 Telegram 机器人在 Telegram 上执行各种任务,例如检索天气更新或新闻标题等信息,以及举办娱乐问答。

它们还可以自动执行任务,如安排提醒事项和执行简单的用户身份验证等。

设置开发环境

本文介绍的 Telegram 机器人可让用户为自己喜欢的城市设置每 6 小时自动更新一次天气,或获取所选任何城市的即时天气信息。

要设置项目目录,请运行以下命令:

# 创建项目目录
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 请求。
  • dotenv:该软件包可帮助你从 .env 文件中加载环境变量。
  • node-schedule:您需要使用该软件包来安排任务和自动更新天气。
  • parse:您需要使用此软件包与 Back4app 解析后台交互。

现在,您已经设置好了开发环境。接下来,您需要在 Telegram 上创建一个机器人账户。

创建 Telegram Bot 账户

Telegram 机器人账户将作为您的机器人在 Telegram 平台上的身份标识。用户可以通过该账户与机器人互动。

要创建 Telegram 机器人,请启动 Telegram 应用程序,在搜索栏中输入 “BotFather”,然后点击已验证的顶部结果。

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 密钥,请登录您的账户(如果没有账户,请创建一个)。

然后,导航至个人资料中的 “我的 API 密钥 “部分,复制您的 API 密钥。

OpenWeatherMap API

.env文件中存储 OpenWeatherMap 的 API 密钥:

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 } 

上述代码块定义了一个getWeather函数,该函数将城市名称作为参数,并从 API 的响应中返回温度和天气描述。

为了每 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 };

上面的代码块创建了两个函数:setCityinit

代码块声明了一个userCities对象,通过使用setCity函数将 Telegram 聊天 ID 映射到城市名称,跟踪机器人用户感兴趣的城市。

该功能可让您的机器人根据用户的偏好,持续接收哪个城市的天气更新。

代码块中的init函数每 6 小时设置一次计划任务,遍历userCities 中存储的所有聊天 ID。init函数使用天气模块获取该城市的当前天气。

您的机器人需要持续保持用户状态,以跟踪不同用户及其偏好的城市。您可以使用 Back4app 的后台服务来实现这一点。

要让机器人跟踪不同用户及其首选城市,可使用 Back4app 的后台服务存储机器人用户的首选城市。

创建 Back4app 后台

要使用 Back4app 后台服务,您需要一个 Back4app 账户。如果您没有账户,可以免费注册

登录您的账户,点击右上角的 “NEW APP “按钮。为您的应用程序命名,然后点击 “创建 “按钮。

创建新的 Back4app 应用程序

点击 “创建 “按钮后,Back4app 将生成您的应用程序,并引导您进入应用程序控制面板。

Back4app 控制面板

从 Node.js 项目连接到 Back4App 上的应用程序实例需要用 Back4app 的应用程序凭据初始化您安装的解析 SDK:应用程序 IDJavascript KEY

点击侧边栏上的 “应用程序设置”,进入 “安全与密钥 “部分,从 Back4app 获取应用程序 IDJavaScript密钥。

将它们保存在.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>"

接下来,您需要创建一个新的 “UserState “类,其中包含两个字段:userId(字符串)和 state(对象)。您将使用 Back4app 的人工智能代理来完成这项任务。

要创建这个带有相应字段的类,请导航到屏幕上的 “人工智能代理 “选项卡,并向代理发出以下提示:

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命令外,机器人还能理解两个主要命令。每次用户交互(包括命令)都会触发用户状态的更新,用户状态和聊天 ID 一起存储在UserState类中。

通过这种状态管理,机器人可以在对话中记住每个用户的特定细节,比如他们偏好的天气更新城市。

要开始使用在 Back4App 数据库中创建的UserState类跟踪用户状态,需要使用凭据初始化 Parse SDK。

将下面的代码块添加到stateManager.js中,用连接到 Back4app Parse 后台所需的凭据初始化 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文件中定义一个 Parse 对象,以表示 Back4app Parse 后端中UserState类的模式,从而让机器人与存储在数据库中的用户状态进行交互。

就像这样

// stateManager.js
const UserState = Parse.Object.extend('UserState');

接下来,您需要让机器人根据数据库中存储的用户 ID 获取特定用户的当前对话状态。

要检索用户的状态,需要使用用户 ID 在UserState类上运行查询,以找到保存在数据库中的状态。

就像这样

// 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对象,设置状态属性,并将该对象保存到数据库中。

接下来,在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, {});
  }
}

该代码块定义了一个ensureUserState函数,用于根据用户 ID 检索状态对象,确保 Back4app Parse 后台的UserState类中存在所提供用户ID 的用户状态数据。

它使用getUserState函数获取用户状态,如果不存在用户状态,则使用setUserState函数将用户状态设置为空对象{}

导出所有函数,以便在其他 Javascript 源文件中使用:

// stateManager.js
module.exports = {
  getUserState,
  setUserState,
  resetUserState,
  ensureUserState,
};

处理用户与机器人的交互

为了让您的机器人能够监听startgetWeathersetCity命令,您需要定义事件监听器,将文本与这三个命令相匹配,以运行回调函数。

这将允许机器人执行与命令相关的任务。

要与 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 });

上面的代码块创建了一个TelegramBot类的新实例,包含两个参数:机器人令牌和一个对象。

该对象会将轮询值设置为true,使机器人能够持续检查 Telegram 服务器的消息和更新。

接下来,导入在本项目中创建的模块。您将使用这些模块来实现机器人的功能,例如获取天气更新、设置首选城市和管理用户状态。

添加以下代码,导入必要的模块:

// bot.js
const weather = require('./weather');
const cityManager = require('./cityManager');
const stateManager = require('./stateManager');

上面的代码块导入了您在所有源文件中创建的函数,使您可以在bot.js文件中调用这些函数。

要设置命令,可以使用TelegramBot类的onText方法。该方法会设置一个监听器来实现回调函数,以执行某些逻辑。

将下面的代码块添加到bot.js文件中,设置一个/start命令处理程序,机器人将通过onText方法主动监听:

// 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);
});

上面的代码块将允许机器人运行一个回调函数,向用户发送一条欢迎信息,详细说明机器人是如何工作的。

机器人使用ensureUserState函数设置用户的初始状态,确保每次用户交互都是清白的。

接下来,将下面的代码块添加到bot.js文件中,设置一个/setCity命令处理程序,机器人将通过onText方法主动监听:

// 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文件中,设置一个/getWeather命令处理程序,机器人将通过onText方法主动监听:

// 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 容器时允许其运行。

创建条目文件

您将创建一个入口文件,用于初始化整个机器人程序。该文件有两个功能。

  • 入口文件将运行机器人的逻辑,需要 JavaScript 文件来处理机器人如何与 Telegram 的 API 交互并响应用户消息。
  • 该入口文件还将设置一个服务器,允许你的机器人在 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 容器上部署。

创建入口文件后,您可以先在本地测试 Telegram 机器人,然后再将其部署到 Back4app 容器中。

测试 Telegram 机器人

运行以下命令启动 Telegram 机器人:

node server.js

要与机器人互动,请打开 Telegram 应用程序,在搜索栏中搜索机器人的名称。进入与机器人的聊天,发送/start命令。

机器人应回复一条欢迎信息和一系列命令。向机器人发送命令

电报机器人的天气信息

通过/setCity命令,机器人可以设置首选城市并定期接收天气更新。

如上图所示,/getWeather命令可以立即获取某个城市的天气信息。

机器人的行为应与上图所示类似,响应您的命令,并按照预期与您互动。

将 Telegram 机器人部署到 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 应用程序,并授予该应用程序访问应用程序代码库所需的权限。

完成 GitHub 配置后,请访问 Back4app 主页并点击屏幕右上角的 “新建应用程序“按钮。

这将带您进入初始化界面,您需要在此选择要创建的应用程序类型。选择 Back4app 代理选项,如下图所示。

在 Back4app 上创建新应用程序

选择 Back4app 代理选项后,您将被重定向到 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"

用实际值替换占位符。上述提示将启动部署过程。完成后,人工智能代理将作出回应,表示部署成功或待定。

您应该会收到类似下面的回复:

人工智能代理响应

如果您收到待部署,您可以在Back4app 容器仪表板上监控应用程序的部署状态。或者,您也可以在 Back4app 容器上手动部署应用程序

结论

在本文中,您将学习如何使用 Node.js 创建 Telegram 机器人。您将学习如何创建 Telegram 机器人账户、处理机器人命令和用户交互。

在 Back4app 人工智能代理的帮助下,您还为机器人创建了一个后台,这使您能够有效管理用户状态,并在对话中存储信息,例如用户喜欢的城市,以便定期接收天气更新。

您可以在此GitHub 代码库中找到完整代码。


Leave a reply

Your email address will not be published.