如何使用Node.js、Telegraf、Jimp和Pexels构建Telegram报价生成器机器人
作为 Write for DOnations 计划的一部分,作者选择了 Free and Open Source Fund 来接受捐赠。
介绍
在本教程中,您将使用 Node.js、telegraf、jimp 和 Pexels API 来构建 Telegram 聊天机器人,它将向您发送一张随机选择的图像,上面覆盖了一个事实。 Telegram 机器人是您可以通过首选 Telegram 客户端使用自定义斜杠命令与之交互的机器人。 您将通过 Telegram 创建机器人,并定义其逻辑以使用 JavaScript 选择随机动物图像和动物事实。
在本教程结束时,您将拥有一个如下所示的 Telegram 聊天机器人:
完成机器人后,每当您发送自定义 Telegram 斜杠命令时,您都会收到有关动物的事实。
先决条件
为了遵循本教程,读者将需要以下工具:
- Node.js 安装在本地,您可以按照【X57X】如何安装Node.js 并创建本地开发环境【X126X】进行。
- 一个 Telegram 帐户,用于注册和选择您喜欢的 Telegram 客户端。
- 一个 Pexels 帐户,可让您下载免费照片和视频。
本教程已使用 Node v12.18.2 和 npm v6.14.8 进行了验证。
第 1 步 - 创建项目根目录
在本节中,您将创建用于构建聊天机器人的目录、创建 Node 项目并安装所需的依赖项。
打开一个终端窗口并创建一个名为 facts-bot
的新目录:
mkdir facts-bot
导航到目录:
cd facts-bot
创建一个名为 temp
的目录:
mkdir temp
使用上面的命令,您创建了一个名为 temp
的目录。 在此目录中,您将临时存储机器人将发送给用户的图像。
现在,您将创建一个新的 Node.js 项目。 运行 npm 的 init
命令将创建一个 package.json
文件,该文件将管理您的依赖项和元数据。
运行初始化命令:
npm init
要接受默认值,在所有提示下按 ENTER
。 或者,您可以个性化您的回复。 为此,请查看教程 如何将 Node.js 模块与 npm 和 package.json 一起使用的第 1 步中的 npm 初始化设置。
打开 package.json 文件并编辑它:
nano package.json
现在,您将更新 package.json
文件中的属性。 用突出显示的代码替换文件中的内容:
包.json
{ "name": "facts-bot", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "start": "nodemon main.js" }, "author": "", "license": "ISC" }
在这里,您更改了 main
和 scripts
属性。 通过更改 main
属性,您已将应用程序主文件设置为 main.js
。 这将通知 Node main.js
文件是程序的主要入口点。 在 scripts
属性中,您添加了一个名为 start
的 script
,它允许您设置启动应用程序时应该运行的命令。 调用 script
后,命令 nodemon
将运行您将在下一步中创建的 main.js
文件。
现在在 package.json
文件中定义了您的设置,您现在将创建一个文件来存储您的环境变量。 在您的终端中,创建一个名为 .env
的文件:
touch .env
在您的 .env
文件中,您将存储 Telegram 机器人令牌和 Pexels API 密钥。 Telegram Bot 令牌允许您与 Telegram bot 交互。 Pexels API 密钥允许您与 Pexels API 交互。 您将在后面的步骤中存储环境变量。
这次,您将使用 npm
安装依赖项 telegraf
、dotenv
、pexels
、jimp
和 [ X112X]。 您还将使用 --save
标志来保存依赖项。 在您的终端中,运行以下命令:
npm install telegraf dotenv pexels jimp uuid --save
在此命令中,您已安装:
- telegraf:一个帮助您使用 JavaScript 或 TypeScript 开发自己的 Telegram 机器人的库。 您将使用它来构建您的机器人。
- dotenv:零依赖模块,将环境变量从
.env
文件加载到process.env
中。 您将使用此模块从您创建的.env
文件中检索机器人令牌和 Pexels API 密钥。 - pexels:一个方便的 Pexels API 包装器,可以在 Node.js 中的服务器和浏览器上使用。 您将使用此模块从 Pexels 检索动物图像。
- jimp:一个完全用 JavaScript 为 Node 编写的图像处理库,外部或本地依赖项为零。 您将使用这个库来编辑从 Pexels 检索到的图像,并在其中插入关于动物的事实。
- uuid:一个允许您在 JavaScript 中生成符合 RFC 的 UUID 的模块。 您将使用此模块为从 Pexels 检索的图像创建一个唯一名称。
现在,安装 nodemon 作为开发依赖项:
npm install nodemon --save-dev
nodemon 是一个开发基于 Node.js 的应用程序的工具,当它检测到目录中的文件更改时,它会自动重新启动 Node 应用程序。 在测试机器人时,您将使用此模块启动并保持应用程序运行。
注意: 在撰写本文时,这些是正在使用的模块的版本:telegraf
: 4.3.0 ; dotenv
:8.2.0; pexels
:1.2.1;jimp
:0.16.1; uuid
:8.3.2; nodemon
:2.0.12。
在此步骤中,您创建了一个项目目录并为您的机器人初始化了一个 Node.js 项目。 您还安装了构建机器人所需的模块。 在下一步中,您将在 Telegram 中注册一个机器人并检索 Pexels API 的 API 密钥。
第 2 步 — 注册您的机器人并从 Pexels API 检索 API 密钥
在本节中,您将首先使用 BotFather 注册一个机器人,然后检索 Pexels API 的 API 密钥。 BotFather 是一个由 Telegram 管理的聊天机器人,允许用户创建和管理聊天机器人。
打开你喜欢的Telegram客户端,搜索@BotFather
,开始聊天。 发送 /newbot
斜杠命令并按照 BotFather 发送的指令进行操作:
选择您的机器人名称和用户名后,您将收到一条包含您的机器人访问令牌的消息:
复制机器人令牌,然后打开您的 .env
文件:
nano .env
将 Bot 令牌保存在名为 BOT_TOKEN
的变量中:
.env
BOT_TOKEN = "Your bot token"
现在您已将机器人令牌保存在 .env
文件中,是时候检索 Pexels API 密钥了。
导航到 Pexels,然后登录到您的 Pexels 帐户。 点击图像和视频 API 选项卡并创建一个新的 API 密钥:
复制 API 密钥,然后打开您的 .env
文件:
nano .env
将 API 密钥保存在名为 PEXELS_API_KEY
的变量中。 您的 .env
应如下所示:
.env
BOT_TOKEN = "Your_bot_token" PEXELS_API_KEY = "Your_Pexels_API_key"
在本节中,您已经注册了您的机器人,检索了您的 Pexels API 密钥,并将您的机器人令牌和 Pexels API 密钥保存在您的 .env
文件中。 在下一部分中,您将创建负责运行机器人的文件。
第 3 步 — 创建 main.js
文件
在本节中,您将创建和构建您的机器人。 您将创建一个带有标签 main.js
的文件,其中将包含您的机器人的逻辑。
在项目的根目录中,使用您喜欢的文本编辑器创建并打开 main.js
文件:
nano main.js
在 main.js
文件中,添加以下代码以导入您将使用的库:
main.js
const { Telegraf } = require('telegraf') const { v4: uuidV4 } = require('uuid') require('dotenv').config() let factGenerator = require('./factGenerator')
在此代码块中,您需要在 telegraf
、uuid
、dotenv
模块和名为 factGenerator.js
的文件中。 您将使用 telegraf
模块来启动和管理机器人,uuid
模块为图像生成唯一的文件名,以及 dotenv
模块来获取您的Telegram 机器人令牌和 Pexels API 密钥存储在 .env
文件中。 factGenerator.js
文件将用于从 Pexels 检索随机动物图像,插入关于动物的事实,并在图像发送给用户后将其删除。 您将在下一节中创建此文件。
在 require
语句下方,添加以下代码以创建机器人实例:
main.js
. . . const bot = new Telegraf(process.env.BOT_TOKEN) bot.start((ctx) => { let message = ` Please use the /fact command to receive a new fact` ctx.reply(message) })
在这里,您检索并使用了 BotFather 发送的 BOT_TOKEN
,创建了一个新的机器人实例,并将其分配给名为 bot
的变量。 创建新的机器人实例后,您为 /start
命令添加了命令侦听器。 此命令负责启动用户和机器人之间的对话。 一旦用户发送包含 /start
的消息,机器人就会回复一条消息,要求用户使用 /fact
命令接收新事实。
您现在已经创建了负责启动与聊天机器人交互的命令处理程序。 现在,让我们创建用于生成事实的命令处理程序。 在 .start()
命令下方,添加以下代码:
main.js
. . . bot.command('fact', async (ctx) => { try { ctx.reply('Generating image, Please wait !!!') let imagePath = `./temp/${uuidV4()}.jpg` await factGenerator.generateImage(imagePath) await ctx.replyWithPhoto({ source: imagePath }) factGenerator.deleteImage(imagePath) } catch (error) { console.log('error', error) ctx.reply('error sending image') } }) bot.launch()
在此代码块中,您为自定义 /fact
斜杠命令创建了一个命令侦听器。 一旦从 Telegram 用户界面触发此命令,机器人就会向用户发送一条消息。 uuid
模块用于生成镜像名称和路径。 图像将存储在您在 Step 1 中创建的 /temp
目录中。 之后,将图像路径传递给您将在 factGenerator.js
文件中定义的名为 generateImage()
的方法,以生成包含动物事实的图像。 生成图像后,将图像发送给用户。 然后,将图像路径传递给 factGenerator.js
文件中名为 deleteFile
的方法来删除图像。 最后,您通过调用 bot.launch()
方法启动了您的机器人。
main.js
文件如下所示:
main.js
const { Telegraf } = require('telegraf') const { v4: uuidV4 } = require('uuid') require('dotenv').config() let factGenerator = require('./factGenerator') const bot = new Telegraf(process.env.BOT_TOKEN) bot.start((ctx) => { let message = ` Please use the /fact command to receive a new fact` ctx.reply(message) }) bot.command('fact', async (ctx) => { try { ctx.reply('Generating image, Please wait !!!') let imagePath = `./temp/${uuidV4()}.jpg` await factGenerator.generateImage(imagePath) await ctx.replyWithPhoto({ source: imagePath }) factGenerator.deleteImage(imagePath) } catch (error) { console.log('error', error) ctx.reply('error sending image') } }); bot.launch()
您已创建负责运行和管理机器人的文件。 您现在将为动物设置事实并在 factGenerator.js
文件中构建机器人的逻辑。
第 4 步 - 创建事实生成器文件并构建机器人逻辑
在本节中,您将创建名为 fact.js
和 factGenerator.js
的文件。 fact.js
将有关动物的事实存储在一个数据源中。 factGenerator.js
文件将包含从文件中检索关于动物的随机事实所需的代码,从 Pexels 检索图像,使用 jimp
将事实写入检索到的图像中,并删除图片。
在项目的根目录中,使用您喜欢的文本编辑器创建并打开 facts.js
文件:
nano facts.js
在 facts.js
文件中添加以下代码来创建您的数据源:
事实.js
const facts = [ { fact: "Mother pandas keep contact with their cub nearly 100% of the time during their first month - with the cub resting on her front and remaining covered by her paw, arm or head.", animal: "Panda" }, { fact: "The elephant's temporal lobe (the area of the brain associated with memory) is larger and denser than that of people - hence the saying 'elephants never forget'.", animal: "Elephant" }, { fact: "On average, males weigh 190kg and females weigh 126kg . They need this weight and power behind them to hunt large prey and defend their pride. ", animal: "Lion" }, { fact: "The Amazon river is home to four species of river dolphin that are found nowhere else on Earth. ", animal: "Dolphin" }, ] module.exports = { facts }
在此代码块中,您定义了一个对象,该对象包含一个包含有关动物的事实的数组,并存储在一个名为 facts
的变量中。 每个对象具有以下属性:fact
和 animal
。 在名为 fact
的属性中,它的值是关于动物的事实,而属性 animal
存储动物的名称。 最后,您要导出 facts
数组。
现在,创建一个名为 factGenerator.js
的文件:
nano factGenerator.js
在 factGenerator.js
文件中,将以下代码添加到依赖项中,您将使用它来构建逻辑以制作动物图像:
factGenerator.js
let { createClient } = require('pexels') let Jimp = require('jimp') const fs = require('fs') let { facts } = require('./facts')
在这里,您需要在 pexels
、jimp
、fs
模块和 facts.js
文件中。 您将使用 pexels
模块从 Pexels 检索动物图像,使用 jimp
模块编辑从 Pexels 检索的图像,并使用 fs
模块从文件中删除图像发送给用户后的目录。
在 require
语句下方,添加以下代码以生成图像:
factGenerator.js
. . . async function generateImage(imagePath) { let fact = randomFact() let photo = await getRandomImage(fact.animal) await editImage(photo, imagePath, fact.fact) }
在此代码块中,您创建了一个名为 generateImage()
的函数。 此函数将文件目录中 Pexel 图像的路径作为参数。 一旦调用了这个函数,就会调用一个名为 randomFact()
的函数,并将返回的值存储在一个名为 fact
的变量中。 randomFact()
函数随机选择 facts.js
文件中的一个对象。 收到对象后,其属性 animal
被传递给名为 getRandomImage()
的函数。 getRandomImage()
函数将使用 pexels
模块搜索包含经过的动物名称的图像,并选择随机图像。 返回的值存储在名为 photo
的变量中。 最后,将 facts.js
文件中的 photo
、imagePath
和 fact
属性传递给名为 editImage()
的函数。 editImage()
函数使用jimp
模块将随机事实插入到随机图像中,然后将编辑后的图像保存在imagePath
中。
在这里,您创建了向机器人发送 /fact
斜杠命令时调用的函数。 现在您将创建函数 getRandomImage()
和 editImage()
并构建选择和编辑随机图像背后的逻辑。
在 generateImage()
函数下方,添加以下代码来设置随机化逻辑:
factGenerator.js
. . . function randomFact() { let fact = facts[randomInteger(0, (facts.length - 1))] return fact } function randomInteger(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
您现在已经创建了函数 randomFact()
和 randomInteger()
。 randomFact()
函数通过调用randomInteger()
函数在facts.js
文件中随机选择一个fact
,并返回该对象。 randomInteger()
函数返回一个 0 区间内的随机整数和 facts.js
文件中的事实数。
现在您已经定义了返回随机事实和随机整数的函数,您需要创建一个函数来从 Pexels 获取随机图像。 在 randomInteger()
函数下方,添加以下代码以获取随机图像:
factGenerator.js
. . . async function getRandomImage(animal) { try { const client = createClient(process.env.PEXELS_API_KEY) const query = animal let image await client.photos.search({ query, per_page: 10 }).then(res => { let images = res.photos image = images[randomInteger(0, (images.length - 1))] }) return image } catch (error) { console.log('error downloading image', error) getRandomImage(animal) } }
在此代码块中,您创建了一个名为 getRandomImage()
的函数。 该函数将动物名称作为参数。 调用此函数时,使用 pexels
模块中的 createClient()
方法对象和存储在 .env
文件中的 Pexels API 密钥创建 client
对象。 动物名称存储在一个名为 query
的变量中,然后 client
对象用于搜索包含 query
中的值的图像。 找到图像后,将借助 randomInteger()
功能选择随机图像。 最后,随机图像返回到您的 main.js
文件中的 generateImage()
方法。
使用 getRandomImage()
功能后,所选图像需要有文本覆盖,然后才能发送到 Telegram 机器人。 在 getRandomImage()
函数下方,添加以下代码来设置覆盖:
factGenerator.js
. . . async function editImage(image, imagePath, fact) { try { let imgURL = image.src.medium let animalImage = await Jimp.read(imgURL).catch(error => console.log('error ', error)) let animalImageWidth = animalImage.bitmap.width let animalImageHeight = animalImage.bitmap.height let imgDarkener = await new Jimp(animalImageWidth, animalImageHeight, '#000000') imgDarkener = await imgDarkener.opacity(0.5) animalImage = await animalImage.composite(imgDarkener, 0, 0); } catch (error) { console.log("error editing image", error) } }
在这里,您创建了一个名为 editImage()
的函数。 此函数将标记为 image
的随机动物、imagePath 和关于此随机动物的 fact
作为参数。 在变量 imgURL
中,从 Pexels API 检索中等尺寸图像的 URL。 然后使用 jimp
的 read()
方法加载图像。 一旦图像被加载并存储在名为 animalImage
的变量中,图像的宽度和高度就会被检索并分别存储在变量 animalImageWidth
和 animalImageHeight
中。 变量 imgDarkener
存储 Jimp()
的新实例并使图像变暗。 jimp
的opacity()
方法用于设置imgDarkener
的不透明度为50%。 最后,使用jimp
的composite()
方法将imgDarkener
中的内容放到animalImage
中的图像之上。 这反过来使 animalImage
中的图像在添加存储在 fact
变量中的文本之前变暗,并使文本在图像上可见。
注意: Jimp 默认提供一个名为 color()
的方法,允许您调整图像的色调级别。 出于本教程的目的,您将编写一个自定义色调调节器,因为 color()
方法不提供此处所需的精度。
在 editImage()
函数内的 try
块的底部,添加以下代码:
factGenerator.js
. . . async function editImage(image, imagePath,fact) { try { . . . let posX = animalImageWidth / 15 let posY = animalImageHeight / 15 let maxWidth = animalImageWidth - (posX * 2) let maxHeight = animalImageHeight - posY let font = await Jimp.loadFont(Jimp.FONT_SANS_16_WHITE) await animalImage.print(font, posX, posY, { text: fact, alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER, alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE }, maxWidth, maxHeight) await animalImage.writeAsync(imagePath) console.log("Image generated successfully") } catch (error) { . . . } }
在此代码块中,您使用了 animalImageWidth
和 animalImageHeight
来获取将用于使 animalImage
中的文本居中的值。 之后,您使用 jimp
的 loadFont()
方法加载字体,并将字体存储在名为 font
的变量中。 字体颜色为白色,类型为sans-serif (SANS
),大小为16。 最后你用jimp
的print()
方法在animalImage
中插入fact
,用write()
方法保存[ imagePath
中的 X135X]。
现在您已经创建了负责编辑图像的函数,您需要一个函数在将图像发送给用户后从文件结构中删除它。 在 editImage()
函数下方,添加以下代码:
factGenerator.js
. . . const deleteImage = (imagePath) => { fs.unlink(imagePath, (err) => { if (err) { return } console.log('file deleted') }) } module.exports = { generateImage, deleteImage }
在这里,您创建了一个名为 deleteImage()
的函数。 该函数将变量 imagePath
作为参数。 一旦调用该函数,fs
模块用于删除存储在变量imagePath
中的图像。 最后,您导出了 generateImage()
函数和 deleteImage()
函数。
设置好函数后,factGenerator.js
文件将如下所示:
factGenerator.js
let { createClient } = require('pexels') let Jimp = require('jimp') const fs = require('fs') let { facts } = require('./facts') async function generateImage(imagePath) { let fact = randomFact() let photo = await getRandomImage(fact.animal) await editImage(photo, imagePath, fact.fact) } function randomFact() { let fact = facts[randomInteger(0, (facts.length - 1))] return fact } function randomInteger(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } async function getRandomImage(animal) { try { const client = createClient(process.env.PEXELS_API_KEY) const query = animal let image await client.photos.search({ query, per_page: 10 }).then(res => { let images = res.photos image = images[randomInteger(0, (images.length - 1))] }) return image } catch (error) { console.log('error downloading image', error) getRandomImage(animal) } } async function editImage(image, imagePath, fact) { try { let imgURL = image.src.medium let animalImage = await Jimp.read(imgURL).catch(error => console.log('error ', error)) let animalImageWidth = animalImage.bitmap.width let animalImageHeight = animalImage.bitmap.height let imgDarkener = await new Jimp(animalImageWidth, animalImageHeight, '#000000') imgDarkener = await imgDarkener.opacity(0.5) animalImage = await animalImage.composite(imgDarkener, 0, 0); let posX = animalImageWidth / 15 let posY = animalImageHeight / 15 let maxWidth = animalImageWidth - (posX * 2) let maxHeight = animalImageHeight - posY let font = await Jimp.loadFont(Jimp.FONT_SANS_16_WHITE) await animalImage.print(font, posX, posY, { text: fact, alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER, alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE }, maxWidth, maxHeight) await animalImage.writeAsync(imagePath) console.log("Image generated successfully") } catch (error) { console.log("error editing image", error) } } const deleteImage = (imagePath) => { fs.unlink(imagePath, (err) => { if (err) { return } console.log('file deleted') }) } module.exports = { generateImage, deleteImage }
保存您的 factGenerator.js
文件。 返回您的终端,然后运行以下命令来启动您的机器人:
npm start
打开您首选的 Telegram 客户端,然后搜索您的机器人。 使用 /start
命令发送消息以启动对话,或单击 Start 按钮。 然后,使用 /fact
命令发送消息以接收您的图像。
您将收到类似于以下内容的图像:
您现在可以在首选的 Telegram 客户端中看到图像,并在图像上添加一个事实。 您已创建负责从 facts.js
文件中检索随机事实、从 Pexels 检索动物图像并将事实插入图像的文件和函数。
结论
在本教程中,您构建了一个 Telegram 聊天机器人,该聊天机器人通过自定义斜线命令发送带有事实覆盖的动物图像。 您通过 telegraf
模块为机器人创建了命令处理程序。 您还创建了函数,负责使用 pexels
模块从 Pexels 中检索随机事实和随机图像,并使用 jimp
模块在随机图像上插入事实。 有关 Pexels API、telegraf
和 jimp
模块的更多信息,请参阅 Pexels API、telegraf、jimp[ X160X]。