Итак у нас есть бот, давайте научим его отправлять аудио-файлы по команде audio. Сперва нужно скопировать аудио-файл в проект и научить бота команде /audio
:
bot.command("audio", (ctx) => {
return ctx.replyWithAudio({ source: "./song.mp3" });
});
Диалог с ботом должен быть похожим на этот:
А теперь давайте научим бот команде, по которой он будет отправлять случайное фото какого-нибудь милого животного. Для этого нужно сперва запастись парой или больше фото. Но есть способ лучше. Можно воспользоваться списком доступных апи, которые могут выдать некое фото. Возьмём для примера https://aws.random.cat/meow .
Приступим к написанию команды:
bot.command("photo", async (ctx) => {
const response = await fetch("https://aws.random.cat/meow");
const data = await response.json();
return ctx.replyWithPhoto(data.file);
});
Всё выглядит логичным, но в NodeJS эта команда не сработает и даже не запустится скрипт. Потому что у него нет команды fetch и нам нужно её установить: npm i node-fetch -S
. Итого, к настоящему времени файл bot.js должен начинаться с таких строк:
require("dotenv").config();
const fetch = require("node-fetch");
const { Telegraf } = require("telegraf");
Напомню: первая строка делает доступным обращение к файлу .env (который можно добавить в .gitignore, тем самым никому не рассказав свой ключ); вторая строка- мы сделали сейчас, подключили команду fetch; третья строка- подключение библиотеки telegraf.
Бот можно запустить командой node bot.js, но давайте сразу же оформим запуск командой. В раздел scripts файла package.json добавьте команду dev-bot:
"scripts": {
"dev-bot": "node edu.js"
},
И теперь после запуска скрипта npm run dev-bot можно убедиться, что всё работает:
Итак, всё у нас работает, можно закончить на этом, но ... выше вы видели список многих апи, которые возвращают фото. Хотелось бы сделать так, что бы бот выдавал фото по какому-то признаку или скачивал по одному из каждого апи и выдавал это альбомом.
Рассмотрим эти возможности по-порядку.
Скачивание фото по признаку
Для этого нужно подготовить сам бот, чтобы он мог понять команду, например, такую: /photo dog
, а если вызывать /photo без параметров, то бот понимал бы, что от него хотят любое фото.
Команда для бота - это, в принципе, текст сообщения, которое начинается с символа / . Поэтому разбор введённой строки может быть таким:
const whatAnimal = ctx.message.text.split(" ")[1] || "";
Здесь мы получаем часть строки, которая начинается после первого пробела. И да, такой подход имеет право быть, но я предложу способ лучше. В telegraf можно написать свой мидлвар, который бы обрабатывал вводимый текст и снабжал наш чат некоей дополнительной информацией. Например- параметры команды.
Вот сейчас мы и напишем такой мидлвар:
const regex = /^\/([^@\s]+)@?(?:(\S+)|)\s?([\s\S]+)?$/i;
/**
* Мидлвар для разбора текста и команд в групповом чате
*/
module.exports = commandParts = async (ctx, next) => {
// В переменную text запишется текст сообщения для бота
const {
message: { text = "" },
} = ctx;
// Разобьём это сообщение на части
const parts = regex.exec(text);
// Если, к примеру, одно слово, то нечего разбивать
if (!parts) return next();
// Сформируем объект command, который присоеденим к ctx.state
const command = {
text,
command: parts[1],
bot: parts[2],
args: parts[3],
get splitArgs() {
return !parts[3] ? [] : parts[3].split(/\s+/).filter((arg) => arg.length);
},
};
ctx.state.command = command;
return next();
};
Сохраните эти строки в файле lib/commandParts.js и подключите их в bot.js:
require("dotenv").config();
const fetch = require("node-fetch");
const { Telegraf } = require("telegraf");
const commandParts = require("../lib/commandParts");
// Создать бота с полученным ключом
const bot = new Telegraf(process.env.TELEGRAM_TOKEN_EDU);
// Подключить мидлвар
bot.use(commandParts);
....
Команда для бота будет выглядеть по-другому:
bot.command("photo", async (ctx) => {
const chatId = ctx.message.chat.id;
// Получение аргументов
const { args = "" } = ctx.state.command;
// Возможно стоит проверить: верные аргументы пришли или нет
const whatAnimal = args;
// Пользователь, не скучай, я начал работу
ctx.telegram.sendMessage(chatId, "Ищу фото ...");
// Запрос урла картинки
const url = await randomAnimal(whatAnimal);
// Предусмотрительно защититься от null, который может внезапно прийти из апи (увы, да)
if (!url) {
return ctx.reply("Поиск фото не удался");
}
// А это что- gif, что ли пришёл, да?
const extension = url.split(".").pop();
if (extension.toLowerCase() === "gif") {
// Если gif, значит оформить анимешку
return telegram.sendAnimation(chatId, url);
}
return ctx.telegram.sendPhoto(chatId, url);
});
Здесь появляется новая функция randomAnimal, которая записана в файле lib/animalPhoto . Вот его листинг:
const fetch = require("node-fetch");
/**
* Случайное фото котофея
*/
const theCatApi = async () => {
const response = await fetch("https://api.thecatapi.com/v1/images/search");
const data = await response.json();
return data.url;
};
/**
* Ещё одно фото котёнка
*/
const randomCat = async () => {
const response = await fetch("https://aws.random.cat/meow");
const data = await response.json();
return data.file;
};
/**
* Собачка
*/
const dogCeo = async () => {
const response = await fetch("https://dog.ceo/api/breeds/image/random");
const data = await response.json();
return data.message;
};
/**
* Собачка 2
*/
const woof = async () => {
const response = await fetch("https://random.dog/woof.json");
const data = await response.json();
return data.url;
};
/**
* Лисичка
*
*/
const randomFox = async () => {
const response = await fetch("https://randomfox.ca/floof/");
const data = await response.json();
return data.image;
};
/**
* Получить случайное фото
* @param {'cat' | 'dog | 'fox'} animal
*/
exports.randomAnimal = async (animal = "") => {
const listApi = [];
if (!animal || animal[0] === "@") {
listApi.push(theCatApi);
listApi.push(randomCat);
listApi.push(dogCeo);
listApi.push(woof);
listApi.push(randomFox);
}
const checkWord = animal.toLowerCase();
if (checkWord === "cat") {
listApi.push(theCatApi);
listApi.push(randomCat);
}
if (checkWord === "dog") {
listApi.push(dogCeo);
listApi.push(woof);
}
if (checkWord === "fox") {
listApi.push(randomFox);
}
if (listApi.length === 0) {
return null;
}
return await listApi[Math.floor(Math.random() * listApi.length)]();
};
О чём эти функции: Если параметр команды не пустой, то обращаемся к случайному апи, сгруппированным по параметру. Иначе- выбирается случайный апи. После вызова возвращается урл картинки.
Итак, в завершении этой заметки файл bot.js теперь стал таким:
require("dotenv").config();
const { Telegraf } = require("telegraf");
const commandParts = require("./lib/commandParts");
const { randomAnimal } = require("./lib/animalPhoto");
// Создать бота с полученным ключом
const bot = new Telegraf(process.env.TELEGRAM_TOKEN_EDU);
// Подключить мидлвар
bot.use(commandParts);
// Обработчик начала диалога с ботом
bot.start((ctx) =>
ctx.reply(
`Приветствую, ${
ctx.from.first_name ? ctx.from.first_name : "хороший человек"
}! Набери /help и увидишь, что я могу.`
)
);
// Обработчик команды /help
bot.help((ctx) => ctx.reply("Справка в процессе"));
// Обработчик команды /whoami
bot.command("whoami", (ctx) => {
const { id, username, first_name, last_name } = ctx.from;
return ctx.replyWithMarkdown(`Кто ты в телеграмме:
*id* : ${id}
*username* : ${username}
*Имя* : ${first_name}
*Фамилия* : ${last_name}
*chatId* : ${ctx.chat.id}`);
});
bot.command("photo", async (ctx) => {
const chatId = ctx.message.chat.id;
// Получение аргументов
const { args = "" } = ctx.state.command;
// Возможно стоит проверить: верные аргументы пришли или нет.
// Но это Вам на домашнее задание ;-)
const whatAnimal = args;
// Пользователь, не скучай, я начал работу
ctx.telegram.sendMessage(chatId, "Ищу фото ...");
// Запрос урла картинки
const url = await randomAnimal(whatAnimal);
// Предусмотрительно защититься от null, который может внезапно прийти из апи (увы, да)
if (!url) {
return ctx.reply("Поиск фото не удался");
}
// А это что- gif, что ли пришёл, да?
const extension = url.split(".").pop();
if (extension.toLowerCase() === "gif") {
// Если gif, значит оформить анимешку
return telegram.sendAnimation(chatId, url);
}
return ctx.telegram.sendPhoto(chatId, url);
});
// Обработчик простого текста
bot.on("text", (ctx) => {
return ctx.reply(ctx.message.text);
});
// Запуск бота
bot.launch();
Работа бота будет такой:
В следующий раз я расскажу, как выдавать фото-альбомы.
Top comments (3)
C:\Users\hilki\OneDrive\Рабочий стол\Telegramm bot JS\bot.js:2
const fetch = require("node-fetch");
^
Error [ERR_REQUIRE_ESM]: require() of ES Module C:\Users\hilki\OneDrive\Рабочий стол\Telegramm bot JS\node_modules\node-fetch\src\index.js from C:\Users\hilki\OneDrive\Рабочий стол\Telegramm bot JS\bot.js not supported.
Instead change the require of index.js in C:\Users\hilki\OneDrive\Рабочий стол\Telegramm bot JS\bot.js to a dynamic import() which is available in all CommonJS modules.
at Object. (C:\Users\hilki\OneDrive\Рабочий стол\Telegramm bot JS\bot.js:2:15) {
code: ←[32m'ERR_REQUIRE_ESM'←[39m
}
Что делать?
Fetch подключил
npm install node-fetch@2
у меня с этим заработало.
С таким не встречался, но в описании написано, что вместо require сделайте import.