telekit-lib 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PUBLISHING.md +52 -0
- package/PUBLISH_GUIDE.md +52 -0
- package/README.md +45 -0
- package/dist/index.d.ts +117 -0
- package/dist/index.js +356 -0
- package/docs/index.html +675 -0
- package/examples/database_bot.ts +77 -0
- package/examples/simple_bot.ts +18 -0
- package/package.json +29 -0
package/PUBLISHING.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# 🚀 Руководство по публикации TeleKit
|
|
2
|
+
|
|
3
|
+
Это руководство объясняет, как опубликовать вашу библиотеку в NPM, на GitHub и разместить документацию.
|
|
4
|
+
|
|
5
|
+
## 1. Подготовка к NPM
|
|
6
|
+
|
|
7
|
+
1. **Войдите в NPM** (если еще не вошли):
|
|
8
|
+
```bash
|
|
9
|
+
npm login
|
|
10
|
+
```
|
|
11
|
+
2. **Соберите проект**:
|
|
12
|
+
```bash
|
|
13
|
+
npm run build
|
|
14
|
+
```
|
|
15
|
+
*Это создаст папку `dist`, которая и будет опубликована.*
|
|
16
|
+
3. **Опубликуйте**:
|
|
17
|
+
```bash
|
|
18
|
+
npm publish --access public
|
|
19
|
+
```
|
|
20
|
+
*Убедитесь, что версия в `package.json` уникальна (например, 2.0.0).*
|
|
21
|
+
|
|
22
|
+
## 2. Отправка на GitHub
|
|
23
|
+
|
|
24
|
+
1. **Инициализируйте Git**:
|
|
25
|
+
```bash
|
|
26
|
+
git init
|
|
27
|
+
git add .
|
|
28
|
+
git commit -m "Initial commit of TeleKit 2.0"
|
|
29
|
+
```
|
|
30
|
+
2. **Отправьте (Push)**:
|
|
31
|
+
```bash
|
|
32
|
+
git remote add origin https://github.com/TaHel-UDev/telekit.git
|
|
33
|
+
git branch -M main
|
|
34
|
+
git push -u origin main
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 3. Хостинг документации (Landing Page)
|
|
38
|
+
|
|
39
|
+
У вас есть красивая документация в стиле "Shadcn" в папке `docs/`.
|
|
40
|
+
|
|
41
|
+
**Вариант А: GitHub Pages (Самый простой)**
|
|
42
|
+
1. Перейдите в настройки репозитория (Repository Settings) > Pages.
|
|
43
|
+
2. Выберите источник: **Deploy from branch**.
|
|
44
|
+
3. Ветка: `main`, Папка: `/docs`.
|
|
45
|
+
4. Нажмите **Save**. Ваш сайт будет доступен по адресу `https://tahel-udev.github.io/telekit/` через несколько минут.
|
|
46
|
+
|
|
47
|
+
**Вариант Б: Netlify / Vercel**
|
|
48
|
+
1. Перетащите папку `docs` в Netlify Drop.
|
|
49
|
+
2. Мгновенное развертывание.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
*Автор TaHel-UDev*
|
package/PUBLISH_GUIDE.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Инструкция по публикации библиотеки в NPM
|
|
2
|
+
|
|
3
|
+
Чтобы вашей библиотекой могли пользоваться другие люди (устанавливать через `npm install easy-tg-bot-lib`), её нужно опубликовать в реестре NPM.
|
|
4
|
+
|
|
5
|
+
## Шаг 1: Подготовка
|
|
6
|
+
1. Убедитесь, что код скомпилирован:
|
|
7
|
+
```bash
|
|
8
|
+
npm run build
|
|
9
|
+
```
|
|
10
|
+
2. Убедитесь, что в `package.json` поле `main` указывает на `dist/index.js`, а `types` на `dist/index.d.ts`. Это уже настроено.
|
|
11
|
+
3. (Опционально) Создайте файл `.npmignore`, чтобы не отправлять в NPM лишние файлы (исходники, конфиги), оставив только папку `dist` и `package.json`.
|
|
12
|
+
Содержимое `.npmignore`:
|
|
13
|
+
```
|
|
14
|
+
src/
|
|
15
|
+
tsconfig.json
|
|
16
|
+
examples/
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Шаг 2: Регистрация в NPM
|
|
20
|
+
Если у вас еще нет аккаунта:
|
|
21
|
+
1. Перейдите на [npmjs.com](https://www.npmjs.com/) и зарегистрируйтесь.
|
|
22
|
+
2. В терминале выполните вход:
|
|
23
|
+
```bash
|
|
24
|
+
npm login
|
|
25
|
+
```
|
|
26
|
+
Следуйте инструкциям (ввод username, password, email).
|
|
27
|
+
|
|
28
|
+
## Шаг 3: Публикация
|
|
29
|
+
1. **Важно**: Имя пакета в `package.json` (`"name": "easy-tg-bot-lib"`) должно быть уникальным во всем NPM. Скорее всего, простые имена уже заняты.
|
|
30
|
+
- Рекомендуется использовать scope (пространство имен), например `@ваш-ник/easy-bot`.
|
|
31
|
+
- Для этого измените имя в `package.json` на `@ваш-ник/easy-bot`.
|
|
32
|
+
2. Выполните команду:
|
|
33
|
+
```bash
|
|
34
|
+
npm publish --access public
|
|
35
|
+
```
|
|
36
|
+
(Флаг `--access public` обязателен для scoped-пакетов, так как по умолчанию они считаются приватными и требуют платной подписки).
|
|
37
|
+
|
|
38
|
+
## Шаг 4: Обновление версий
|
|
39
|
+
Когда вы внесете изменения в код:
|
|
40
|
+
1. Измените версию в `package.json` (например, с `1.0.0` на `1.0.1`).
|
|
41
|
+
2. Снова выполните `npm run build`.
|
|
42
|
+
3. Снова выполните `npm publish`.
|
|
43
|
+
|
|
44
|
+
## Как будут устанавливать вашу библиотеку
|
|
45
|
+
Пользователи будут писать:
|
|
46
|
+
```bash
|
|
47
|
+
npm install @ваш-ник/easy-bot
|
|
48
|
+
```
|
|
49
|
+
И использовать:
|
|
50
|
+
```typescript
|
|
51
|
+
import { EasyBot } from '@ваш-ник/easy-bot';
|
|
52
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# TeleKit Framework ⚡
|
|
2
|
+
|
|
3
|
+
**Профессиональный TypeScript фреймворк для Telegram ботов**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/telekit-lib)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
TeleKit — это идеальный инструмент для создания Telegram ботов. Он сочетает в себе архитектуру корпоративного уровня с простотой, доступной для новичков.
|
|
9
|
+
|
|
10
|
+
## ✨ Особенности
|
|
11
|
+
|
|
12
|
+
- **LocalDB**: Встроенная JSON NoSQL база данных. Не нужно настраивать Mongo или SQL для старта.
|
|
13
|
+
- **Строгая типизация**: Полная поддержка TypeScript.
|
|
14
|
+
- **Сцены и Визарды**: Легкое создание многошаговых форм.
|
|
15
|
+
- **Клавиатуры**: Простой конструктор разметки.
|
|
16
|
+
- **Адаптеры**: Простое подключение к Redis/Postgres.
|
|
17
|
+
|
|
18
|
+
## 📦 Установка
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install telekit-lib
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 📖 Документация
|
|
25
|
+
|
|
26
|
+
Полная документация доступна в папке [`docs/`](./docs). Откройте `docs/index.html` в вашем браузере.
|
|
27
|
+
|
|
28
|
+
## ⚡ Быстрый старт
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { TeleKit, LocalDB } from 'telekit-lib';
|
|
32
|
+
|
|
33
|
+
const bot = new TeleKit(process.env.TOKEN);
|
|
34
|
+
const db = new LocalDB('users.json');
|
|
35
|
+
|
|
36
|
+
bot.command('start', (ctx) => {
|
|
37
|
+
db.push({ id: ctx.from.id, name: ctx.from.first_name });
|
|
38
|
+
ctx.reply('Добро пожаловать в TeleKit!');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
bot.start();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
*Создано TaHel-UDev*
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Update, User, InlineKeyboardButton, ReplyKeyboardMarkup, InlineKeyboardMarkup } from 'typegram';
|
|
2
|
+
export * from 'typegram';
|
|
3
|
+
export type Handler = (ctx: Context) => void | Promise<void>;
|
|
4
|
+
export type Middleware = (ctx: Context, next: () => Promise<void>) => Promise<void>;
|
|
5
|
+
interface SessionContainer {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Простая, но мощная JSON база данных.
|
|
10
|
+
* Позволяет хранить массивы данных с поиском и фильтрацией.
|
|
11
|
+
*/
|
|
12
|
+
export declare class LocalDB<T extends object> {
|
|
13
|
+
private filePath;
|
|
14
|
+
private data;
|
|
15
|
+
constructor(filename: string);
|
|
16
|
+
private load;
|
|
17
|
+
private save;
|
|
18
|
+
/** Добавить запись */
|
|
19
|
+
push(item: T): Promise<void>;
|
|
20
|
+
/** Найти одну запись */
|
|
21
|
+
findOne(predicate: (item: T) => boolean): Promise<T | null>;
|
|
22
|
+
/** Найти все подходящие записи */
|
|
23
|
+
find(predicate: (item: T) => boolean): Promise<T[]>;
|
|
24
|
+
/** Обновить запись (находим и меняем поля) */
|
|
25
|
+
update(predicate: (item: T) => boolean, updates: Partial<T>): Promise<boolean>;
|
|
26
|
+
/** Удалить запись */
|
|
27
|
+
delete(predicate: (item: T) => boolean): Promise<void>;
|
|
28
|
+
/** Получить все записи */
|
|
29
|
+
getAll(): T[];
|
|
30
|
+
}
|
|
31
|
+
export interface ISessionStore {
|
|
32
|
+
get(key: string): Promise<any | null>;
|
|
33
|
+
set(key: string, value: any): Promise<void>;
|
|
34
|
+
delete(key: string): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export declare class MemoryStore implements ISessionStore {
|
|
37
|
+
private store;
|
|
38
|
+
get(key: string): Promise<any>;
|
|
39
|
+
set(key: string, value: any): Promise<void>;
|
|
40
|
+
delete(key: string): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export declare class FileStore implements ISessionStore {
|
|
43
|
+
private filePath;
|
|
44
|
+
constructor(filename?: string);
|
|
45
|
+
get(key: string): Promise<any>;
|
|
46
|
+
set(key: string, value: any): Promise<void>;
|
|
47
|
+
delete(key: string): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
export declare class Scene {
|
|
50
|
+
id: string;
|
|
51
|
+
steps: Handler[];
|
|
52
|
+
constructor(id: string, ...steps: Handler[]);
|
|
53
|
+
}
|
|
54
|
+
export declare class Keyboard {
|
|
55
|
+
static reply(buttons: (string | string[])[], options?: {
|
|
56
|
+
resize_keyboard?: boolean;
|
|
57
|
+
one_time_keyboard?: boolean;
|
|
58
|
+
}): ReplyKeyboardMarkup;
|
|
59
|
+
static inline(rows: InlineKeyboardButton[][]): InlineKeyboardMarkup;
|
|
60
|
+
static callback(text: string, data: string): InlineKeyboardButton;
|
|
61
|
+
static url(text: string, url: string): InlineKeyboardButton;
|
|
62
|
+
static remove(): {
|
|
63
|
+
remove_keyboard: boolean;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export declare class Context {
|
|
67
|
+
update: Update;
|
|
68
|
+
bot: TeleKit;
|
|
69
|
+
session: SessionContainer;
|
|
70
|
+
constructor(update: Update, bot: TeleKit, session: SessionContainer);
|
|
71
|
+
get chatId(): number | undefined;
|
|
72
|
+
get from(): User | undefined;
|
|
73
|
+
get text(): string | undefined;
|
|
74
|
+
get callbackData(): string | undefined;
|
|
75
|
+
reply(text: string, extra?: any): Promise<any>;
|
|
76
|
+
answerCallback(text?: string, alert?: boolean): Promise<any>;
|
|
77
|
+
editMessageText(text: string, extra?: any): Promise<any>;
|
|
78
|
+
deleteMessage(): Promise<any>;
|
|
79
|
+
enter(sceneId: string): void;
|
|
80
|
+
next(): void;
|
|
81
|
+
leave(): void;
|
|
82
|
+
}
|
|
83
|
+
export declare class TelegramApi {
|
|
84
|
+
private client;
|
|
85
|
+
constructor(token: string);
|
|
86
|
+
call(method: string, data?: any): Promise<any>;
|
|
87
|
+
sendMessage(params: any): Promise<any>;
|
|
88
|
+
}
|
|
89
|
+
export declare class TeleKit {
|
|
90
|
+
api: TelegramApi;
|
|
91
|
+
storage: ISessionStore;
|
|
92
|
+
scenes: Map<string, Scene>;
|
|
93
|
+
private token;
|
|
94
|
+
private offset;
|
|
95
|
+
private isPolling;
|
|
96
|
+
private middlewares;
|
|
97
|
+
private commands;
|
|
98
|
+
private textPatterns;
|
|
99
|
+
private callbackPatterns;
|
|
100
|
+
private fallbackHandler;
|
|
101
|
+
constructor(token: string, options?: {
|
|
102
|
+
store?: ISessionStore;
|
|
103
|
+
});
|
|
104
|
+
setCommands(commands: {
|
|
105
|
+
command: string;
|
|
106
|
+
description: string;
|
|
107
|
+
}[]): Promise<any>;
|
|
108
|
+
use(middleware: Middleware): void;
|
|
109
|
+
addScene(scene: Scene): void;
|
|
110
|
+
command(cmd: string, handler: Handler): void;
|
|
111
|
+
onText(trigger: string | RegExp, handler: Handler): void;
|
|
112
|
+
onCallback(trigger: string | RegExp, handler: Handler): void;
|
|
113
|
+
onMessage(handler: Handler): void;
|
|
114
|
+
start(): Promise<void>;
|
|
115
|
+
private loop;
|
|
116
|
+
private handleUpdate;
|
|
117
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
36
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
|
+
};
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.TeleKit = exports.TelegramApi = exports.Context = exports.Keyboard = exports.Scene = exports.FileStore = exports.MemoryStore = exports.LocalDB = void 0;
|
|
43
|
+
const axios_1 = __importDefault(require("axios"));
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
__exportStar(require("typegram"), exports);
|
|
47
|
+
// --- 📦 LOCAL DB SYSTEM (Встроенная база) ---
|
|
48
|
+
/**
|
|
49
|
+
* Простая, но мощная JSON база данных.
|
|
50
|
+
* Позволяет хранить массивы данных с поиском и фильтрацией.
|
|
51
|
+
*/
|
|
52
|
+
class LocalDB {
|
|
53
|
+
constructor(filename) {
|
|
54
|
+
this.data = [];
|
|
55
|
+
this.filePath = path.resolve(process.cwd(), filename);
|
|
56
|
+
this.load();
|
|
57
|
+
}
|
|
58
|
+
load() {
|
|
59
|
+
if (!fs.existsSync(this.filePath)) {
|
|
60
|
+
this.save();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
this.data = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
console.error(`Ошибка чтения БД ${this.filePath}:`, e);
|
|
68
|
+
this.data = [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
save() {
|
|
72
|
+
fs.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2));
|
|
73
|
+
}
|
|
74
|
+
// --- Public API ---
|
|
75
|
+
/** Добавить запись */
|
|
76
|
+
async push(item) {
|
|
77
|
+
this.data.push(item);
|
|
78
|
+
this.save();
|
|
79
|
+
}
|
|
80
|
+
/** Найти одну запись */
|
|
81
|
+
async findOne(predicate) {
|
|
82
|
+
return this.data.find(predicate) || null;
|
|
83
|
+
}
|
|
84
|
+
/** Найти все подходящие записи */
|
|
85
|
+
async find(predicate) {
|
|
86
|
+
return this.data.filter(predicate);
|
|
87
|
+
}
|
|
88
|
+
/** Обновить запись (находим и меняем поля) */
|
|
89
|
+
async update(predicate, updates) {
|
|
90
|
+
const item = this.data.find(predicate);
|
|
91
|
+
if (item) {
|
|
92
|
+
Object.assign(item, updates);
|
|
93
|
+
this.save();
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
/** Удалить запись */
|
|
99
|
+
async delete(predicate) {
|
|
100
|
+
const initLength = this.data.length;
|
|
101
|
+
this.data = this.data.filter(item => !predicate(item));
|
|
102
|
+
if (this.data.length !== initLength)
|
|
103
|
+
this.save();
|
|
104
|
+
}
|
|
105
|
+
/** Получить все записи */
|
|
106
|
+
getAll() {
|
|
107
|
+
return [...this.data];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.LocalDB = LocalDB;
|
|
111
|
+
class MemoryStore {
|
|
112
|
+
constructor() {
|
|
113
|
+
this.store = new Map();
|
|
114
|
+
}
|
|
115
|
+
async get(key) { return this.store.get(key) || null; }
|
|
116
|
+
async set(key, value) { this.store.set(key, value); }
|
|
117
|
+
async delete(key) { this.store.delete(key); }
|
|
118
|
+
}
|
|
119
|
+
exports.MemoryStore = MemoryStore;
|
|
120
|
+
class FileStore {
|
|
121
|
+
constructor(filename = 'sessions.json') {
|
|
122
|
+
this.filePath = path.resolve(process.cwd(), filename);
|
|
123
|
+
}
|
|
124
|
+
async get(key) {
|
|
125
|
+
if (!fs.existsSync(this.filePath))
|
|
126
|
+
return null;
|
|
127
|
+
try {
|
|
128
|
+
const d = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
|
|
129
|
+
return d[key] || null;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async set(key, value) {
|
|
136
|
+
let d = {};
|
|
137
|
+
if (fs.existsSync(this.filePath)) {
|
|
138
|
+
try {
|
|
139
|
+
d = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
|
|
140
|
+
}
|
|
141
|
+
catch { }
|
|
142
|
+
}
|
|
143
|
+
d[key] = value;
|
|
144
|
+
fs.writeFileSync(this.filePath, JSON.stringify(d, null, 2));
|
|
145
|
+
}
|
|
146
|
+
async delete(key) {
|
|
147
|
+
if (!fs.existsSync(this.filePath))
|
|
148
|
+
return;
|
|
149
|
+
const d = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
|
|
150
|
+
delete d[key];
|
|
151
|
+
fs.writeFileSync(this.filePath, JSON.stringify(d, null, 2));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.FileStore = FileStore;
|
|
155
|
+
// --- Классы ---
|
|
156
|
+
class Scene {
|
|
157
|
+
constructor(id, ...steps) { this.id = id; this.steps = steps; }
|
|
158
|
+
}
|
|
159
|
+
exports.Scene = Scene;
|
|
160
|
+
class Keyboard {
|
|
161
|
+
static reply(buttons, options = { resize_keyboard: true }) {
|
|
162
|
+
const keyboard = buttons.map(row => {
|
|
163
|
+
if (typeof row === 'string')
|
|
164
|
+
return [{ text: row }];
|
|
165
|
+
return row.map(text => ({ text }));
|
|
166
|
+
});
|
|
167
|
+
return { keyboard, ...options };
|
|
168
|
+
}
|
|
169
|
+
static inline(rows) { return { inline_keyboard: rows }; }
|
|
170
|
+
static callback(text, data) { return { text, callback_data: data }; }
|
|
171
|
+
static url(text, url) { return { text, url }; }
|
|
172
|
+
static remove() { return { remove_keyboard: true }; }
|
|
173
|
+
}
|
|
174
|
+
exports.Keyboard = Keyboard;
|
|
175
|
+
class Context {
|
|
176
|
+
constructor(update, bot, session) {
|
|
177
|
+
this.update = update;
|
|
178
|
+
this.bot = bot;
|
|
179
|
+
this.session = session;
|
|
180
|
+
}
|
|
181
|
+
get chatId() {
|
|
182
|
+
if ('message' in this.update)
|
|
183
|
+
return this.update.message.chat.id;
|
|
184
|
+
if ('callback_query' in this.update)
|
|
185
|
+
return this.update.callback_query.message?.chat.id;
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
get from() {
|
|
189
|
+
if ('message' in this.update)
|
|
190
|
+
return this.update.message.from;
|
|
191
|
+
if ('callback_query' in this.update)
|
|
192
|
+
return this.update.callback_query.from;
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
get text() {
|
|
196
|
+
if ('message' in this.update && 'text' in this.update.message)
|
|
197
|
+
return this.update.message.text;
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
get callbackData() {
|
|
201
|
+
if ('callback_query' in this.update && 'data' in this.update.callback_query)
|
|
202
|
+
return this.update.callback_query.data;
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
async reply(text, extra = {}) {
|
|
206
|
+
if (!this.chatId)
|
|
207
|
+
return;
|
|
208
|
+
return this.bot.api.sendMessage({ chat_id: this.chatId, text, ...extra });
|
|
209
|
+
}
|
|
210
|
+
async answerCallback(text, alert = false) {
|
|
211
|
+
if (!('callback_query' in this.update))
|
|
212
|
+
return;
|
|
213
|
+
return this.bot.api.call('answerCallbackQuery', { callback_query_id: this.update.callback_query.id, text, show_alert: alert });
|
|
214
|
+
}
|
|
215
|
+
async editMessageText(text, extra = {}) {
|
|
216
|
+
if (!('callback_query' in this.update) || !this.update.callback_query.message)
|
|
217
|
+
return;
|
|
218
|
+
const msg = this.update.callback_query.message;
|
|
219
|
+
return this.bot.api.call('editMessageText', { chat_id: msg.chat.id, message_id: msg.message_id, text, ...extra });
|
|
220
|
+
}
|
|
221
|
+
async deleteMessage() {
|
|
222
|
+
if (this.chatId) {
|
|
223
|
+
const msgId = 'message' in this.update
|
|
224
|
+
? this.update.message.message_id
|
|
225
|
+
: ('callback_query' in this.update ? this.update.callback_query.message?.message_id : undefined);
|
|
226
|
+
if (msgId)
|
|
227
|
+
return this.bot.api.call('deleteMessage', { chat_id: this.chatId, message_id: msgId });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
enter(sceneId) {
|
|
231
|
+
this.session.__scene = { currentSceneId: sceneId, currentStep: 0, data: {} };
|
|
232
|
+
const scene = this.bot.scenes.get(sceneId);
|
|
233
|
+
if (scene && scene.steps.length > 0)
|
|
234
|
+
scene.steps[0](this);
|
|
235
|
+
}
|
|
236
|
+
next() { if (this.session.__scene)
|
|
237
|
+
this.session.__scene.currentStep++; }
|
|
238
|
+
leave() { this.session.__scene = null; }
|
|
239
|
+
}
|
|
240
|
+
exports.Context = Context;
|
|
241
|
+
class TelegramApi {
|
|
242
|
+
constructor(token) { this.client = axios_1.default.create({ baseURL: `https://api.telegram.org/bot${token}/`, timeout: 40000 }); }
|
|
243
|
+
async call(method, data = {}) {
|
|
244
|
+
try {
|
|
245
|
+
const res = await this.client.post(method, data);
|
|
246
|
+
if (!res.data.ok)
|
|
247
|
+
throw new Error(res.data.description);
|
|
248
|
+
return res.data.result;
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
console.error(`API Error [${method}]:`, e.message);
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async sendMessage(params) { return this.call('sendMessage', params); }
|
|
256
|
+
}
|
|
257
|
+
exports.TelegramApi = TelegramApi;
|
|
258
|
+
class TeleKit {
|
|
259
|
+
constructor(token, options = {}) {
|
|
260
|
+
this.scenes = new Map();
|
|
261
|
+
this.offset = 0;
|
|
262
|
+
this.isPolling = false;
|
|
263
|
+
this.middlewares = [];
|
|
264
|
+
this.commands = new Map();
|
|
265
|
+
this.textPatterns = new Map();
|
|
266
|
+
this.callbackPatterns = new Map();
|
|
267
|
+
this.fallbackHandler = null;
|
|
268
|
+
this.token = token;
|
|
269
|
+
this.api = new TelegramApi(token);
|
|
270
|
+
this.storage = options.store || new MemoryStore();
|
|
271
|
+
}
|
|
272
|
+
async setCommands(commands) { return this.api.call('setMyCommands', { commands }); }
|
|
273
|
+
use(middleware) { this.middlewares.push(middleware); }
|
|
274
|
+
addScene(scene) { this.scenes.set(scene.id, scene); }
|
|
275
|
+
command(cmd, handler) { this.commands.set(cmd.replace('/', ''), handler); }
|
|
276
|
+
onText(trigger, handler) { this.textPatterns.set(trigger, handler); }
|
|
277
|
+
onCallback(trigger, handler) { this.callbackPatterns.set(trigger, handler); }
|
|
278
|
+
onMessage(handler) { this.fallbackHandler = handler; }
|
|
279
|
+
async start() {
|
|
280
|
+
this.isPolling = true;
|
|
281
|
+
console.log('⚡ TeleKit Bot started...');
|
|
282
|
+
this.loop();
|
|
283
|
+
}
|
|
284
|
+
async loop() {
|
|
285
|
+
if (!this.isPolling)
|
|
286
|
+
return;
|
|
287
|
+
try {
|
|
288
|
+
const updates = await this.api.call('getUpdates', { offset: this.offset, timeout: 30 });
|
|
289
|
+
if (updates && Array.isArray(updates)) {
|
|
290
|
+
for (const update of updates) {
|
|
291
|
+
this.offset = update.update_id + 1;
|
|
292
|
+
await this.handleUpdate(update);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch (e) {
|
|
297
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
298
|
+
}
|
|
299
|
+
if (this.isPolling)
|
|
300
|
+
this.loop();
|
|
301
|
+
}
|
|
302
|
+
async handleUpdate(update) {
|
|
303
|
+
let userId;
|
|
304
|
+
if ('message' in update)
|
|
305
|
+
userId = update.message.from?.id;
|
|
306
|
+
else if ('callback_query' in update)
|
|
307
|
+
userId = update.callback_query.from.id;
|
|
308
|
+
if (!userId)
|
|
309
|
+
return;
|
|
310
|
+
const sessionKey = `session:${userId}`;
|
|
311
|
+
let sessionData = await this.storage.get(sessionKey) || {};
|
|
312
|
+
const ctx = new Context(update, this, sessionData);
|
|
313
|
+
let handled = false;
|
|
314
|
+
for (const mw of this.middlewares)
|
|
315
|
+
await mw(ctx, async () => { });
|
|
316
|
+
if (ctx.session.__scene) {
|
|
317
|
+
const s = ctx.session.__scene;
|
|
318
|
+
const scene = this.scenes.get(s.currentSceneId);
|
|
319
|
+
if (scene && scene.steps[s.currentStep]) {
|
|
320
|
+
await scene.steps[s.currentStep](ctx);
|
|
321
|
+
handled = true;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (!handled) {
|
|
325
|
+
if (ctx.callbackData) {
|
|
326
|
+
for (const [t, h] of this.callbackPatterns)
|
|
327
|
+
if (typeof t === 'string' ? ctx.callbackData === t : t.test(ctx.callbackData)) {
|
|
328
|
+
await h(ctx);
|
|
329
|
+
handled = true;
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (!handled && ctx.text) {
|
|
334
|
+
if (ctx.text.startsWith('/')) {
|
|
335
|
+
const cmd = ctx.text.split(' ')[0].substring(1);
|
|
336
|
+
if (this.commands.has(cmd)) {
|
|
337
|
+
await this.commands.get(cmd)(ctx);
|
|
338
|
+
handled = true;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (!handled) {
|
|
342
|
+
for (const [t, h] of this.textPatterns)
|
|
343
|
+
if (typeof t === 'string' ? ctx.text === t : t.test(ctx.text)) {
|
|
344
|
+
await h(ctx);
|
|
345
|
+
handled = true;
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (!handled && this.fallbackHandler && ctx.text)
|
|
351
|
+
await this.fallbackHandler(ctx);
|
|
352
|
+
}
|
|
353
|
+
await this.storage.set(sessionKey, ctx.session);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
exports.TeleKit = TeleKit;
|