Skip to content

TypeScript и виды клавиатур

Что сделаем?

  • Научимся запускать TypeScript файлы с помощью TSX
  • Типизируем process.env
  • Познакомимся с клавиатурой и её видами
  • Научимся отвечать на обычные сообщения и нажатия на инлайн клавиатуру

Запуск TypeScript файлов в Node.js

Не бойтесь. Никаких особенностей TypeScript кроме как подсказок мы сейчас использовать не будем.

На момент написания этого гайда в Node.js только-только появляется нативная поддержка запуска файлов TypeScript (Подробнее), но сейчас использовать это рано. Поэтому мы воспользуемся прекрасной библиотекой «TSX».

Для начала нам необходимо поменять расширение у файла index.js с JavaScript на TypeScript (то есть переименовать в index.ts). А после мы можем опробовать tsx с помощью простой команды:

bash
npx tsx watch --env-file=.env index.ts

Довольно просто) Теперь давайте заменим этой командой содержимое dev команды в нашем package.json файле.

Типизируем process.env

К сожалению, TypeScript не знает что у нас находится в файле .env поэтому и подсказать не может. После переименования файла у вас должна была появиться ошибка у использования переменной среды BOT_TOKEN, поэтому давайте поправим это, добавив в конце файла:

ts
declare global {
    namespace NodeJS {
        interface ProcessEnv {
            BOT_TOKEN: string;
        }
    }
}

Не пугайтесь, то что мы проделали называется Declaration merging, то есть мы объединили TypeScript интерфейс типов для process.env с необходимыми нам значениями в глобальной зоне видимости. Пока можете принять это как есть) Главное что ошибка исчезла и появились подсказки!

ts
process
.
env
.B
;

Знакомство с клавиатурой и её видами

Раздел в документации о клавиатуре

Keyboard

Раздел в документации об этом виде клавиатуры

Давайте напишем обработчик команды /start и отправим пользователю обычную клавиатуру.

ts
import { Bot, Keyboard } from "gramio";

const bot = new Bot(process.env.BOT_TOKEN)
    .command("start", (context) =>
        context.send("Вы написали /start", {
            reply_markup: new Keyboard().text("Пинг"),
        })
    )
    .hears("Пинг", (context) => context.send("Понг"));

bot.start();

Давайте разберёмся что такое new Keyboard().text("Пинг").

ts
new Keyboard().text("Пинг");

Эта конструкция написанная с удобный классом Keyboard прячет за собой страшные вещи и на самом деле телеграм получает это уже так:

json
{
    "keyboard": [
        [
            {
                "text": "Пинг"
            }
        ]
    ],
    "one_time_keyboard": false,
    "is_persistent": false,
    "selective": false,
    "resize_keyboard": true
}

А hears это метод, который в отличие от command слушает все сообщения.

InlineKeyboard

Раздел в документации об этом виде клавиатуры

Теперь же давайте познаем всю мощь InlineKeyboard! Давайте вместе с «Понг» будем отправлять пользователю инлайн клавиатуру!

ts
import { Bot, Keyboard, InlineKeyboard } from "gramio";

const bot = new Bot(process.env.BOT_TOKEN)
    .command("start", (context) =>
        context.send("Вы написали /start", {
            reply_markup: new Keyboard().text("Пинг"),
        })
    )
    .hears("Пинг", (context) =>
        context.send("Понг", {
            reply_markup: new InlineKeyboard().text("Пинг", "ping"),
        })
    );

bot.start();

Вы спросите а что за 2 аргумент в функции text, а я отвечу что это payload. Грубо говоря, строка, которую нам вернёт телеграм, когда будет оповещать о нажатой кнопке. Так что давайте обработаем это событие. Оно называется callback_query и у GramIO уже есть удобный шорт-хенд для этого!

ts
import { Bot, Keyboard, InlineKeyboard } from "gramio";

const bot = new Bot(process.env.BOT_TOKEN)
    .command("start", (context) =>
        context.send("Вы написали /start", {
            reply_markup: new Keyboard().text("Пинг"),
        })
    )
    .hears("Пинг", (context) =>
        context.send("Понг", {
            reply_markup: new InlineKeyboard().text("Пинг", "ping"),
        })
    )
    .callbackQuery("ping", (context) => {
        await context.answer("По лбу понг");

        return context.editText("Понг, но уже из callback_query!");
    });

bot.start();

Теперь в ответ на нажатие кнопки с payload равным ping редактируем текст сообщения (кнопки при этом удаляются) и показываем всплывающее окно.

RemoveKeyboard

Раздел в документации об этом виде клавиатуры

Теперь давайте научимся удалять кнопки если пользователь напишем «Понг»

ts
import { Bot, Keyboard, InlineKeyboard, RemoveKeyboard } from "gramio";

const bot = new Bot(process.env.BOT_TOKEN)
    .command("start", (context) =>
        context.send("Вы написали /start", {
            reply_markup: new Keyboard().text("Пинг"),
        })
    )
    .hears("Пинг", (context) =>
        context.send("Понг", {
            reply_markup: new InlineKeyboard().text("Пинг", "ping"),
        })
    )
    .hears("Понг", (context) =>
        context.send("А ты оригинален", {
            reply_markup: new RemoveKeyboard(),
        })
    )
    .callbackQuery("ping", async (context) => {
        await context.answer("По лбу понг");

        return context.editText("Понг, но уже из callback_query!");
    });

bot.start();

ForceReply

Раздел в документации об этом виде клавиатуры

С помощью этого вида кнопок (телеграм считает их кнопками) вы можете сделать так чтобы пользователь автоматически ответил на отправленное вами сообщение, а в поле placeholder мы запишем значение, которое Телеграм покажет пользователю вместо стандартного «Написать сообщение...»

ts
import {
    Bot,
    Keyboard,
    InlineKeyboard,
    RemoveKeyboard,
    ForceReplyKeyboard,
} from "gramio";

const bot = new Bot(process.env.BOT_TOKEN)
    .command("start", (context) =>
        context.send("Вы написали /start", {
            reply_markup: new Keyboard().text("Пинг").text("Вопрос"),
        })
    )
    .hears("Вопрос", (context) => {
        return context.send("Введите ответ", {
            reply_markup: new ForceReplyKeyboard().placeholder(
                "Введите своё ответ прямо сюда!!!"
            ),
        });
    })
    .hears("Пинг", (context) =>
        context.send("Понг", {
            reply_markup: new InlineKeyboard().text("Пинг", "ping"),
        })
    )
    .hears("Понг", (context) =>
        context.send("А ты оригинален", {
            reply_markup: new RemoveKeyboard(),
        })
    )
    .callbackQuery("ping", async (context) => {
        await context.answer("По лбу понг");

        return context.editText("Понг, но уже из callback_query!");
    });

bot.start();

На сегодня всё. Надеюсь ещё встретимся!

  • BOT_TOKEN