@2byte/tgbot-framework 1.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/README.md +301 -0
- package/bin/2byte-cli.ts +85 -0
- package/package.json +50 -0
- package/src/cli/CreateBotCommand.ts +182 -0
- package/src/cli/GenerateCommand.ts +112 -0
- package/src/cli/InitCommand.ts +108 -0
- package/src/console/migrate.ts +83 -0
- package/src/core/App.ts +1016 -0
- package/src/core/BotArtisan.ts +80 -0
- package/src/core/BotMigration.ts +31 -0
- package/src/core/BotSeeder.ts +67 -0
- package/src/core/utils.ts +3 -0
- package/src/illumination/Artisan.ts +149 -0
- package/src/illumination/InlineKeyboard.ts +44 -0
- package/src/illumination/Message2Byte.ts +254 -0
- package/src/illumination/Message2ByteLiveProgressive.ts +278 -0
- package/src/illumination/Message2bytePool.ts +108 -0
- package/src/illumination/Migration.ts +186 -0
- package/src/illumination/RunSectionRoute.ts +85 -0
- package/src/illumination/Section.ts +430 -0
- package/src/illumination/SectionComponent.ts +64 -0
- package/src/illumination/Telegraf2byteContext.ts +33 -0
- package/src/index.ts +33 -0
- package/src/libs/TelegramAccountControl.ts +523 -0
- package/src/types.ts +172 -0
- package/src/user/UserModel.ts +132 -0
- package/src/user/UserStore.ts +119 -0
- package/templates/bot/.env.example +18 -0
- package/templates/bot/artisan.ts +9 -0
- package/templates/bot/bot.ts +74 -0
- package/templates/bot/database/dbConnector.ts +5 -0
- package/templates/bot/database/migrate.ts +10 -0
- package/templates/bot/database/migrations/001_create_users.sql +17 -0
- package/templates/bot/database/seed.ts +15 -0
- package/templates/bot/package.json +31 -0
- package/templates/bot/sectionList.ts +7 -0
- package/templates/bot/sections/HomeSection.ts +63 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { UserAttributes, UserServiceAttributes } from '../types';
|
|
2
|
+
import { UserStore } from './UserStore';
|
|
3
|
+
|
|
4
|
+
export class UserModel {
|
|
5
|
+
attributes: UserAttributes;
|
|
6
|
+
serviceAttributes: UserServiceAttributes = {
|
|
7
|
+
lastActive: new Date(),
|
|
8
|
+
lastMessageIds: [],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
private userSession: UserStore | null = null;
|
|
12
|
+
|
|
13
|
+
constructor(attributes: UserAttributes) {
|
|
14
|
+
this.attributes = attributes;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
setSessionStorage(storage: UserStore): this {
|
|
18
|
+
this.userSession = storage;
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
upActive(): void {
|
|
23
|
+
this.serviceAttributes.lastActive = new Date();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
storeMessageId(messageId: number, limit: number): this {
|
|
27
|
+
if (this.serviceAttributes.lastMessageIds.indexOf(messageId) === -1) {
|
|
28
|
+
this.serviceAttributes.lastMessageIds.push(messageId);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (this.serviceAttributes.lastMessageIds.length > limit) {
|
|
32
|
+
this.serviceAttributes.lastMessageIds.shift();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getAttributes(): UserAttributes {
|
|
39
|
+
return this.attributes;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get id(): number | undefined {
|
|
43
|
+
return this.attributes.id;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get lastMessageIds(): number[] {
|
|
47
|
+
return this.serviceAttributes.lastMessageIds;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get lastMessageId(): number | undefined {
|
|
51
|
+
const ids = this.serviceAttributes.lastMessageIds;
|
|
52
|
+
return ids.length ? ids[ids.length - 1] : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
removeMessageId(messageId: number): this {
|
|
56
|
+
const index = this.serviceAttributes.lastMessageIds.indexOf(messageId);
|
|
57
|
+
if (index !== -1) {
|
|
58
|
+
this.serviceAttributes.lastMessageIds.splice(index, 1);
|
|
59
|
+
}
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static async existsOnServer(tgUsername: string, tgId: number): Promise<boolean> {
|
|
64
|
+
// Здесь должен быть запрос к API, но мы оставим это для будущей реализации
|
|
65
|
+
// const user = await api.fetch("user/get/" + tgUsername + "/" + tgId);
|
|
66
|
+
// return !user.error;
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static async register(params: { tgUsername: string; tgId: number; tgName: string; userRefid?: number }): Promise<UserModel> {
|
|
71
|
+
// Здесь должен быть запрос к API, но мы оставим это для будущей реализации
|
|
72
|
+
// let resApi = await api.fetch("user/register", "post", {
|
|
73
|
+
// tg_username: params.tgUsername,
|
|
74
|
+
// tg_id: params.tgId,
|
|
75
|
+
// name: params.tgName,
|
|
76
|
+
// user_refid: params.userRefid,
|
|
77
|
+
// });
|
|
78
|
+
|
|
79
|
+
// return UserModel.make(resApi.data);
|
|
80
|
+
|
|
81
|
+
return UserModel.make({
|
|
82
|
+
tg_id: params.tgId,
|
|
83
|
+
tg_username: params.tgUsername,
|
|
84
|
+
tg_name: params.tgName
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static async findOnServer(tgUsername: string): Promise<UserModel> {
|
|
89
|
+
// Здесь должен быть запрос к API, но мы оставим это для будущей реализации
|
|
90
|
+
// const user = await api.fetch("user/get/" + tgUsername);
|
|
91
|
+
// return UserModel.make(user.data);
|
|
92
|
+
|
|
93
|
+
return UserModel.make({
|
|
94
|
+
tg_id: 0,
|
|
95
|
+
tg_username: tgUsername,
|
|
96
|
+
tg_name: tgUsername
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async refresh(): Promise<boolean> {
|
|
101
|
+
if (this.userSession) {
|
|
102
|
+
return this.userSession.add(
|
|
103
|
+
this.attributes.tg_username,
|
|
104
|
+
await UserModel.findOnServer(this.attributes.tg_username)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async refreshAttributes(): Promise<this> {
|
|
111
|
+
this.attributes = (await UserModel.findOnServer(this.attributes.tg_username)).attributes;
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static make(attributes: UserAttributes): UserModel {
|
|
116
|
+
return new UserModel(attributes);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static errorHandler(error: any, metaInfo?: any): void {
|
|
120
|
+
if (error instanceof Error) {
|
|
121
|
+
if (metaInfo) {
|
|
122
|
+
console.error("Error addons:", metaInfo);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get username(): string {
|
|
130
|
+
return this.attributes.tg_username;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { UserModel } from './UserModel';
|
|
2
|
+
import { UserSession } from '../types';
|
|
3
|
+
import { EnvVars } from '../types';
|
|
4
|
+
|
|
5
|
+
interface ApiConfig {
|
|
6
|
+
apiUrl: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Api {
|
|
10
|
+
fetch(endpoint: string, method?: string, data?: any): Promise<any>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class UserStore {
|
|
14
|
+
private usersMap: Map<string, UserModel> = new Map();
|
|
15
|
+
private usersSession: Map<UserModel, UserSession> = new Map();
|
|
16
|
+
private api: Api | null = null;
|
|
17
|
+
|
|
18
|
+
constructor(envConfig?: EnvVars) {
|
|
19
|
+
if (envConfig) {
|
|
20
|
+
let url = envConfig.BOT_API_URL || '';
|
|
21
|
+
|
|
22
|
+
// Здесь должна быть инициализация API, но мы оставим это для будущей реализации
|
|
23
|
+
// this.api = new Api({
|
|
24
|
+
// apiUrl: url.endsWith('/') ? url : url + '/',
|
|
25
|
+
// });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async checkOrRegister(params: { tgUsername: string, tgId: number, tgName: string }): Promise<any> {
|
|
30
|
+
if (!this.usersMap.has(params.tgUsername) && this.api) {
|
|
31
|
+
let userData = null;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
let resApi = await this.api.fetch('user/get/' + params.tgUsername);
|
|
35
|
+
userData = resApi.data;
|
|
36
|
+
} catch (err) {
|
|
37
|
+
let resApi = await this.api.fetch('user/register', 'post', {
|
|
38
|
+
tg_username: params.tgUsername,
|
|
39
|
+
tg_id: params.tgId,
|
|
40
|
+
name: params.tgName,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
userData = resApi.data;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (userData !== null) {
|
|
47
|
+
this.add(params.tgUsername, userData);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return userData;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
find(tgUsername: string): UserModel {
|
|
56
|
+
return this.usersMap.get(tgUsername) as UserModel;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
findSession(userModel: UserModel): UserSession {
|
|
60
|
+
return this.usersSession.get(userModel) || {};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
exists(tgUsername: string): boolean {
|
|
64
|
+
return this.usersMap.has(tgUsername);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
add(tgUsername: string, data: UserModel | any): boolean {
|
|
68
|
+
if (data instanceof UserModel) {
|
|
69
|
+
this.usersMap.set(tgUsername, data);
|
|
70
|
+
} else {
|
|
71
|
+
this.usersMap.set(tgUsername, new UserModel(data));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.usersSession.set(this.usersMap.get(tgUsername) as UserModel, {});
|
|
75
|
+
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
upActive(tgUsername: string): this {
|
|
80
|
+
const user = this.usersMap.get(tgUsername);
|
|
81
|
+
if (user) {
|
|
82
|
+
user.upActive();
|
|
83
|
+
}
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
storeMessageId(tgUsername: string, messageId: number, limit: number): this {
|
|
88
|
+
const user = this.usersMap.get(tgUsername);
|
|
89
|
+
if (user) {
|
|
90
|
+
user.storeMessageId(messageId, limit);
|
|
91
|
+
}
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
getLastMessageId(tgUsername: string): number | undefined {
|
|
96
|
+
const user = this.usersMap.get(tgUsername);
|
|
97
|
+
if (user) {
|
|
98
|
+
const ids = user.lastMessageIds;
|
|
99
|
+
return ids.length ? ids[ids.length - 1] : undefined;
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
autocleanup(minutes: number = 1): void {
|
|
105
|
+
const getNotActiveUsers = (): [string, UserModel][] => {
|
|
106
|
+
return Array.from(this.usersMap).filter(([tgUsername, data]) => {
|
|
107
|
+
return (
|
|
108
|
+
data.serviceAttributes.lastActive.getTime() <= Date.now() - minutes * 60 * 1000
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
setInterval(() => {
|
|
114
|
+
getNotActiveUsers().forEach(([tgUsername]) => {
|
|
115
|
+
this.usersMap.delete(tgUsername);
|
|
116
|
+
});
|
|
117
|
+
}, 60 * 1000);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Переменные окружения для {{className}} Bot
|
|
2
|
+
|
|
3
|
+
# Токен бота от @BotFather
|
|
4
|
+
BOT_TOKEN=your_bot_token_here
|
|
5
|
+
|
|
6
|
+
# Путь к базе данных
|
|
7
|
+
DATABASE_PATH=./database/database.sqlite
|
|
8
|
+
|
|
9
|
+
# Настройки логирования
|
|
10
|
+
LOG_LEVEL=info
|
|
11
|
+
|
|
12
|
+
BOT_API_URL=https://localhost:3000
|
|
13
|
+
APP_ENV=local
|
|
14
|
+
BOT_DEV_HOT_RELOAD_SECTIONS=true
|
|
15
|
+
BOT_ACCESS=public
|
|
16
|
+
ACCESS_USERNAMES=stork0
|
|
17
|
+
BOT_HOOK_DOMAIN=example.com
|
|
18
|
+
BOT_HOOK_PORT=3000
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { App } from '@2byte/tgbot-framework';
|
|
2
|
+
import { UserStore } from "@2byte/tgbot-framework";
|
|
3
|
+
import { sectionList } from "./sectionList";
|
|
4
|
+
import { Database } from 'bun:sqlite';
|
|
5
|
+
import { EnvVars } from "@2byte/tgbot-framework";
|
|
6
|
+
|
|
7
|
+
if (import.meta.dirname === undefined) {
|
|
8
|
+
throw new Error("import.meta.dirname is not defined. Ensure you are using a module environment.");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// check if the environment is set up correctly
|
|
12
|
+
const requiredEnvVars: (keyof EnvVars)[] = [
|
|
13
|
+
"BOT_TOKEN",
|
|
14
|
+
"BOT_API_URL",
|
|
15
|
+
"BOT_HOOK_DOMAIN",
|
|
16
|
+
"BOT_HOOK_PORT",
|
|
17
|
+
];
|
|
18
|
+
for (const envVar of requiredEnvVars) {
|
|
19
|
+
if (!process.env[envVar]) {
|
|
20
|
+
throw new Error(`${envVar} is not set in the environment variables.`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare global {
|
|
25
|
+
var userStorage: UserStore;
|
|
26
|
+
var db: Database;
|
|
27
|
+
var settings: Record<string, any>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Initialize global variables
|
|
31
|
+
global.userStorage = new UserStore(process.env as EnvVars);
|
|
32
|
+
global.db = new Database(import.meta.dirname +'/database/database.sqlite');
|
|
33
|
+
global.settings = {
|
|
34
|
+
paging: {
|
|
35
|
+
taskListPerPage: 3, // Number items per page
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Autoclean storage user by timeout
|
|
40
|
+
userStorage.autocleanup(10);
|
|
41
|
+
|
|
42
|
+
const appController = new App.Builder()
|
|
43
|
+
.envConfig(process.env as EnvVars)
|
|
44
|
+
.botToken(process.env.BOT_TOKEN as string)
|
|
45
|
+
.telegrafConfigLaunch({
|
|
46
|
+
// webhook: {
|
|
47
|
+
// // Public domain for webhook; e.g.: example.com
|
|
48
|
+
// domain: process.env.BOT_HOOK_DOMAIN,
|
|
49
|
+
// // Port to listen on; e.g.: 8080
|
|
50
|
+
// port: +process.env.BOT_HOOK_PORT,
|
|
51
|
+
// // Path to listen on nginx
|
|
52
|
+
// path: "/super-mega-tgbot",
|
|
53
|
+
// secretToken: process.env.BOT_HOOK_SECRET_TOKEN,
|
|
54
|
+
// },
|
|
55
|
+
})
|
|
56
|
+
.apiUrl(process.env.BOT_API_URL as string)
|
|
57
|
+
.settings(settings)
|
|
58
|
+
.userStorage(userStorage)
|
|
59
|
+
.sections(sectionList)
|
|
60
|
+
.keepSectionInstances(true)
|
|
61
|
+
.debug()
|
|
62
|
+
//.telegrafLog()
|
|
63
|
+
.devHotReloadSections(process.env.BOT_DEV_HOT_RELOAD_SECTIONS === "true")
|
|
64
|
+
.mainMenuKeyboard([
|
|
65
|
+
["🏠 Лобби"],
|
|
66
|
+
])
|
|
67
|
+
.hears({
|
|
68
|
+
"🏠 Лобби": "home",
|
|
69
|
+
})
|
|
70
|
+
.terminateSigInt()
|
|
71
|
+
.terminateSigTerm()
|
|
72
|
+
.build();
|
|
73
|
+
|
|
74
|
+
(await appController.init()).launch();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BotMigration } from '@2byte/tgbot-framework';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const migration = new BotMigration({
|
|
5
|
+
botPath: __dirname,
|
|
6
|
+
migrationsPath: path.join(__dirname, 'migrations'),
|
|
7
|
+
databasePath: path.join(__dirname, 'database.sqlite')
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
migration.run();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- UP
|
|
2
|
+
CREATE TABLE IF NOT EXISTS `users` (
|
|
3
|
+
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
4
|
+
`role` TEXT DEFAULT 'user',
|
|
5
|
+
`tg_id` INTEGER NOT NULL UNIQUE,
|
|
6
|
+
`username` TEXT,
|
|
7
|
+
`first_name` TEXT,
|
|
8
|
+
`last_name` TEXT,
|
|
9
|
+
`is_banned_by_user` INTEGER DEFAULT 0,
|
|
10
|
+
`is_banned_by_admin` INTEGER DEFAULT 0,
|
|
11
|
+
`banned_reason` TEXT,
|
|
12
|
+
`language` TEXT DEFAULT 'en',
|
|
13
|
+
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
14
|
+
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
15
|
+
);
|
|
16
|
+
-- DOWN
|
|
17
|
+
DROP TABLE IF EXISTS `users`;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BotSeeder } from '@2byte/tgbot-framework';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// Импортируйте здесь ваши сидеры
|
|
5
|
+
// import { seedUsers } from './seeds/users';
|
|
6
|
+
|
|
7
|
+
const seeder = new BotSeeder({
|
|
8
|
+
botPath: __dirname,
|
|
9
|
+
databasePath: path.join(__dirname, 'database.sqlite'),
|
|
10
|
+
seeders: [
|
|
11
|
+
// seedUsers,
|
|
12
|
+
]
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
seeder.run();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{kebabName}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{description}}",
|
|
5
|
+
"main": "bot.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "bun --watch bot.ts",
|
|
8
|
+
"start": "bun bot.ts",
|
|
9
|
+
"build": "bun build bot.ts --outdir ./dist",
|
|
10
|
+
"migrate": "bun database/migrate.ts",
|
|
11
|
+
"artisan": "bun artisan.ts",
|
|
12
|
+
"seed": "bun database/seed.ts",
|
|
13
|
+
"seed:clear": "bun database/seed.ts --clear",
|
|
14
|
+
"seed:clean": "bun database/seed.ts --clean-only"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"telegram",
|
|
18
|
+
"bot",
|
|
19
|
+
"2byte"
|
|
20
|
+
],
|
|
21
|
+
"author": "{{author}}",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@2byte/tgbot-framework": "^1.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.19.8",
|
|
28
|
+
"bun-types": "^1.2.18",
|
|
29
|
+
"typescript": "^5.8.3"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Section, SectionOptions, InlineKeyboard } from "@2byte/tgbot-framework";
|
|
2
|
+
|
|
3
|
+
export default class HomeSection extends Section {
|
|
4
|
+
static command = "home";
|
|
5
|
+
static description = "{{className}} Bot Home section";
|
|
6
|
+
static actionRoutes = {
|
|
7
|
+
"home.index": "index",
|
|
8
|
+
"home.help": "help",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
public sectionId = "home";
|
|
12
|
+
private mainInlineKeyboard: InlineKeyboard;
|
|
13
|
+
|
|
14
|
+
constructor(options: SectionOptions) {
|
|
15
|
+
super(options);
|
|
16
|
+
|
|
17
|
+
this.mainInlineKeyboard = this.makeInlineKeyboard([
|
|
18
|
+
[this.makeInlineButton("ℹ️ Помощь", "home.help")],
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async up(): Promise<void> {}
|
|
23
|
+
public async down(): Promise<void> {}
|
|
24
|
+
public async setup(): Promise<void> {}
|
|
25
|
+
public async unsetup(): Promise<void> {}
|
|
26
|
+
|
|
27
|
+
async index() {
|
|
28
|
+
const message = `
|
|
29
|
+
🏠 **{{className}} Bot**
|
|
30
|
+
|
|
31
|
+
Добро пожаловать в {{className}} бот!
|
|
32
|
+
Это стартовая секция, созданная с помощью 2byte framework.
|
|
33
|
+
|
|
34
|
+
Выберите действие:
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
await this.message(message)
|
|
38
|
+
.inlineKeyboard(this.mainInlineKeyboard)
|
|
39
|
+
.send();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async help() {
|
|
43
|
+
const message = `
|
|
44
|
+
ℹ️ **Помощь**
|
|
45
|
+
|
|
46
|
+
Это бот, созданный с помощью 2byte Telegram Bot Framework.
|
|
47
|
+
|
|
48
|
+
Доступные команды:
|
|
49
|
+
• /start - Главное меню
|
|
50
|
+
|
|
51
|
+
Для разработчиков:
|
|
52
|
+
• bun run artisan make:section <name> - Создать секцию
|
|
53
|
+
• bun run migrate - Выполнить миграции
|
|
54
|
+
• bun run seed - Заполнить данными
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
await this.message(message)
|
|
58
|
+
.inlineKeyboard([
|
|
59
|
+
[this.makeInlineButton("🏠 На главную", "home.index")],
|
|
60
|
+
])
|
|
61
|
+
.send();
|
|
62
|
+
}
|
|
63
|
+
}
|