TON Connect для Telegram ботов
Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @alexgton.
В этом руководстве описан устаревший метод интеграции TON Connect с ботами Telegram. Для более безопасного и актуального способа рекомендуется использовать [Telegram Mini Apps](/v3/guidelines/dapps/tma/overview для более современной и безопасной интеграции.
В этом руководстве мы создадим пример Telegram-бота с использованием JavaScript TON Connect SDK, поддерживающего аутентификацию TON Connect 2.0. Мы рассмотрим подключение кошельков, отправку транзакций, получение информации о кошельках и отключение кошельков.
Открыть демо-бот
Ознакомиться с GitHub
Ссылки на документацию
- [Документация TON Connect SDK] (https://www.npmjs.com/package/@tonconnect/sdk)
Необходимые компоненты
- Вам нужно создать Telegram-бота с помощью @BotFather и сохранить его токен.
- Необходимо установить Node JS (в этом руководстве используется версия 18.1.0).
- Необ ходимо установить Docker.
Создание проекта
Настройка зависимостей
Начнем с создания проекта Node.js. Мы будем использовать TypeScript и библиотеку node-telegram-bot-api, хотя вы можете выбрать другую библиотеку по своему усмотрению. Также мы будем использовать библиотеку qrcode для генерации QR-кодов, но вы можете заменить её любой другой аналогичной библиотекой.
Давайте создадим директорию ton-connect-bot
. Добавьте в нее следующий файл package.json:
{
"name": "ton-connect-bot",
"version": "1.0.0",
"scripts": {
"compile": "npx rimraf dist && tsc",
"run": "node ./dist/main.js"
},
"dependencies": {
"@tonconnect/sdk": "^3.0.0-beta.1",
"dotenv": "^16.0.3",
"node-telegram-bot-api": "^0.61.0",
"qrcode": "^1.5.1"
},
"devDependencies": {
"@types/node-telegram-bot-api": "^0.61.4",
"@types/qrcode": "^1.5.0",
"rimraf": "^3.0.2",
"typescript": "^4.9.5"
}
}
Запустите npm i
, чтобы установить зависимости.
Добавле ние файла tsconfig.json
Создайте файл tsconfig.json
:
Код tsconfig.json
{
"compilerOptions": {
"declaration": true,
"lib": ["ESNext", "dom"],
"resolveJsonModule": true,
"experimentalDecorators": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"target": "es6",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": false,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"useUnknownInCatchVariables": false,
"noUncheckedIndexedAccess": true,
"emitDecoratorMetadata": false,
"importHelpers": false,
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"allowJs": true,
"outDir": "./dist"
},
"include": ["src"],
"exclude": [
"./tests","node_modules", "lib", "types"]
}
Добавление простого кода бота
Создайте файл .env
и добавьте туда токен вашего бота, манифест DApp и список кошельков, которые будут кэшироваться в это время:
[Подробнее о tonconnect-manifes.json] (https://github.com/ton-connect/sdk/tree/main/packages/sdk#add-the-tonconnect-manifest)
# .env
TELEGRAM_BOT_TOKEN=<YOUR BOT TOKEN, E.G 1234567890:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>
TELEGRAM_BOT_LINK=<YOUR TG BOT LINK HERE, E.G. https://t.me/ton_connect_example_bot>
MANIFEST_URL=https://raw.githubusercontent.com/ton-connect/demo-telegram-bot/master/tonconnect-manifest.json
WALLETS_LIST_CACHE_TTL_MS=86400000
Создайте директорию src
и файл bot.ts
внутри. Давайте создадим там экземпляр TelegramBot:
// src/bot.ts
import TelegramBot from 'node-telegram-bot-api';
import * as process from 'process';
const token = process.env.TELEGRAM_BOT_TOKEN!;
export const bot = new TelegramBot(token, { polling: true });
Теперь мы можем создать входной файл main.ts
в директории src
:
// src/main.ts
import dotenv from 'dotenv';
dotenv.config();
import { bot } from './bot';
bot.on('message', msg => {
const chatId = msg.chat.id;
bot.sendMessage(chatId, 'Received your message');
});
Все готово. Вы можете запустить npm run compile
и npm run start
, и отправить любое сообщение вашему боту. Бот ответит: "Received your message". Мы готовы к интеграции с TonConnect.
На данный момент у нас следующая структура файлов:
ton-connect-bot
├── src
│ ├── bot.ts
│ └── main.ts
├── package.json
├── package-lock.json
├── .env
└── tsconfig.json
Подключение кошелька
После установки @tonconnect/sdk
мы можем начать с его импорта для инициализации подключений кошелька.
Мы начнем с получения списка кошельков. Нам нужны только кошельки, совместимые с http-bridge. Создайте папку ton-connect
в src
и добавьте туда файл wallets.ts
:
Мы также определим функцию getWalletInfo
, которая запрашивает подробную информацию о кошельке по его appName
.
Разница между name
и appName
заключается в том, что name
- это читаемое название кошелька, а appName
- это уникальный идентификатор кошелька.
// src/ton-connect/wallets.ts
import { isWalletInfoRemote, WalletInfoRemote, WalletsListManager } from '@tonconnect/sdk';
const walletsListManager = new WalletsListManager({
cacheTTLMs: Number(process.env.WALLETS_LIST_CACHE_TTL_MS)
});
export async function getWallets(): Promise<WalletInfoRemote[]> {
const wallets = await walletsListManager.getWallets();
return wallets.filter(isWalletInfoRemote);
}
export async function getWalletInfo(walletAppName: string): Promise<WalletInfo | undefined> {
const wallets = await getWallets();
return wallets.find(wallet => wallet.appName.toLowerCase() === walletAppName.toLowerCase());
}
Теперь нам нужно определить хранилище TonConnect. TonConnect использует localStorage
для сохранения данных о подключении при работе в браузере, однако в среде NodeJS localStorage
отсутствует. Поэтому нам нужно добавить собственную простую реализацию хранилища.
Подробнее о хранилище TonConnect
Создайте файл storage.ts
в директории ton-connect
:
// src/ton-connect/storage.ts
import { IStorage } from '@tonconnect/sdk';
const storage = new Map<string, string>(); // temporary storage implementation. We will replace it with the redis later
export class TonConnectStorage implements IStorage {
constructor(private readonly chatId: number) {} // we need to have different stores for different users
private getKey(key: string): string {
return this.chatId.toString() + key; // we will simply have different keys prefixes for different users
}
async removeItem(key: string): Promise<void> {
storage.delete(this.getKey(key));
}
async setItem(key: string, value: string): Promise<void> {
storage.set(this.getKey(key), value);
}
async getItem(key: string): Promise<string | null> {
return storage.get(this.getKey(key)) || null;
}
}
Мы переходим к реализации подключения кошелька.
Измените src/main.ts
и добавьте команду connect
. Подключение кошелька будет реализовано в обработчике этой команды.
import dotenv from 'dotenv';
dotenv.config();
import { bot } from './bot';
import { getWallets } from './ton-connect/wallets';
import TonConnect from '@tonconnect/sdk';
import { TonConnectStorage } from './ton-connect/storage';
import QRCode from 'qrcode';
bot.onText(/\/connect/, async msg => {
const chatId = msg.chat.id;
const wallets = await getWallets();
const connector = new TonConnect({
storage: new TonConnectStorage(chatId),
manifestUrl: process.env.MANIFEST_URL
});
connector.onStatusChange(wallet => {
if (wallet) {
bot.sendMessage(chatId, `${wallet.device.appName} wallet connected!`);
}
});
const tonkeeper = wallets.find(wallet => wallet.appName === 'tonkeeper')!;
const link = connector.connect({
bridgeUrl: tonkeeper.bridgeUrl,
universalLink: tonkeeper.universalLink
});
const image = await QRCode.toBuffer(link);
await bot.sendPhoto(chatId, image);
});
Давайте разберемся, что мы делаем. Сначала мы получаем список кошельков и создаем экземпляр TonConnect.
Затем подписываемся на изменение кошелька. Когда пользователь подключит кошелек, бот отправит сообщение ${wallet.device.appName} wallet connected!
.
Далее мы находим кошелек Tonkeeper и создаем ссылку для подключения. В завершение мы генерируем QR-код со ссылкой и отправляем его пользователю в виде фотографии.
Теперь вы можете запустить бота (npm run compile
и npm run start
), а затем отправить боту сообщение /connect
. Бот должен ответить QR-кодом. Отсканируйте его кошельком Tonkeeper. В чате появится сообщение Tonkeeper wallet connected!
.
Мы будем использовать коннектор во многих местах, поэтому давайте перенесем код его создания в отдельный файл:
// src/ton-connect/connector.ts
import TonConnect from '@tonconnect/sdk';
import { TonConnectStorage } from './storage';
import * as process from 'process';
export function getConnector(chatId: number): TonConnect {
return new TonConnect({
manifestUrl: process.env.MANIFEST_URL,
storage: new TonConnectStorage(chatId)
});
}
И импортируем его в src/main.ts
// src/main.ts
import dotenv from 'dotenv';
dotenv.config();
import { bot } from './bot';
import { getWallets } from './ton-connect/wallets';
import QRCode from 'qrcode';
import { getConnector } from './ton-connect/connector';
bot.onText(/\/connect/, async msg => {
const chatId = msg.chat.id;
const wallets = await getWallets();
const connector = getConnector(chatId);
connector.onStatusChange(wallet => {
if (wallet) {
bot.sendMessage(chatId, `${wallet.device.appName} wallet connected!`);
}
});
const tonkeeper = wallets.find(wallet => wallet.appName === 'tonkeeper')!;
const link = connector.connect({
bridgeUrl: tonkeeper.bridgeUrl,
universalLink: tonkeeper.universalLink
});
const image = await QRCode.toBuffer(link);
await bot.sendPhoto(chatId, image);
});
На данный момент у нас следующая структура файлов:
bot-demo
├── src
│ ├── ton-connect
│ │ ├── connector.ts
│ │ ├── wallets.ts
│ │ └── storage.ts
│ ├── bot.ts
│ └── main.ts
├── package.json
├── package-lock.json
├── .env
└── tsconfig.json