blockmine 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.
Files changed (61) hide show
  1. package/README.md +107 -0
  2. package/backend/cli.js +40 -0
  3. package/backend/nodemon.json +12 -0
  4. package/backend/package-lock.json +2539 -0
  5. package/backend/package.json +34 -0
  6. package/backend/prisma/migrations/20250614085849_add_bot_note/migration.sql +126 -0
  7. package/backend/prisma/migrations/20250614153037_add_plugin_installed_date/migration.sql +27 -0
  8. package/backend/prisma/migrations/migration_lock.toml +3 -0
  9. package/backend/prisma/schema.prisma +138 -0
  10. package/backend/prisma/seed.js +60 -0
  11. package/backend/src/api/routes/bots.js +777 -0
  12. package/backend/src/api/routes/permissions.js +79 -0
  13. package/backend/src/api/routes/plugins.js +110 -0
  14. package/backend/src/api/routes/servers.js +50 -0
  15. package/backend/src/api/routes/settings.js +40 -0
  16. package/backend/src/core/BotManager.js +264 -0
  17. package/backend/src/core/BotProcess.js +233 -0
  18. package/backend/src/core/DependencyService.js +93 -0
  19. package/backend/src/core/MessageQueue.js +126 -0
  20. package/backend/src/core/PermissionManager.js +97 -0
  21. package/backend/src/core/PluginLoader.js +64 -0
  22. package/backend/src/core/PluginManager.js +161 -0
  23. package/backend/src/core/PluginService.js +57 -0
  24. package/backend/src/core/UserService.js +181 -0
  25. package/backend/src/core/commands/ping.js +35 -0
  26. package/backend/src/core/commands/warn.js +40 -0
  27. package/backend/src/core/ipc/PermissionManager.stub.js +24 -0
  28. package/backend/src/core/ipc/UserService.stub.js +31 -0
  29. package/backend/src/core/system/Command.js +53 -0
  30. package/backend/src/core/system/CommandHandler.js +98 -0
  31. package/backend/src/core/system/CommandManager.js +59 -0
  32. package/backend/src/core/system/CommandRegistry.js +21 -0
  33. package/backend/src/core/system/parseArguments.js +43 -0
  34. package/backend/src/real-time/socketHandler.js +31 -0
  35. package/backend/src/server.js +66 -0
  36. package/frontend/dist/apple-touch-icon.png +0 -0
  37. package/frontend/dist/assets/index-B83SHIXE.css +1 -0
  38. package/frontend/dist/assets/index-Dh-PcVh1.js +8179 -0
  39. package/frontend/dist/favicon-96x96.png +0 -0
  40. package/frontend/dist/favicon.ico +0 -0
  41. package/frontend/dist/favicon.svg +3 -0
  42. package/frontend/dist/index.html +51 -0
  43. package/frontend/dist/logo.png +0 -0
  44. package/frontend/dist/logo.svg +178 -0
  45. package/frontend/dist/monacoeditorwork/css.worker.bundle.js +53462 -0
  46. package/frontend/dist/monacoeditorwork/editor.worker.bundle.js +13519 -0
  47. package/frontend/dist/monacoeditorwork/html.worker.bundle.js +29662 -0
  48. package/frontend/dist/monacoeditorwork/json.worker.bundle.js +21320 -0
  49. package/frontend/dist/monacoeditorwork/ts.worker.bundle.js +256353 -0
  50. package/frontend/dist/site.webmanifest +21 -0
  51. package/frontend/dist/vite.svg +1 -0
  52. package/frontend/dist/web-app-manifest-192x192.png +0 -0
  53. package/frontend/dist/web-app-manifest-512x512.png +0 -0
  54. package/frontend/package.json +65 -0
  55. package/image/1.png +0 -0
  56. package/image/2.png +0 -0
  57. package/image/3.png +0 -0
  58. package/image/logo.png +0 -0
  59. package/package.json +27 -0
  60. package/tailwind.config.js +0 -0
  61. package/vite.config.js +0 -0
@@ -0,0 +1,79 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const { PrismaClient } = require('@prisma/client');
4
+ const prisma = new PrismaClient();
5
+
6
+
7
+ router.get('/groups', async (req, res) => {
8
+ const groups = await prisma.group.findMany({ include: { permissions: { include: { permission: true } } } });
9
+ res.json(groups);
10
+ });
11
+
12
+ router.post('/groups', async (req, res) => {
13
+ const { name, permissionIds } = req.body;
14
+ const newGroup = await prisma.group.create({
15
+ data: {
16
+ name,
17
+ permissions: {
18
+ create: permissionIds.map(id => ({ permissionId: id }))
19
+ }
20
+ }
21
+ });
22
+ res.status(201).json(newGroup);
23
+ });
24
+
25
+
26
+
27
+ router.put('/groups/:id', async (req, res) => {
28
+ const groupId = parseInt(req.params.id);
29
+ const { name, permissionIds } = req.body;
30
+ try {
31
+ await prisma.$transaction(async (tx) => {
32
+ await tx.group.update({ where: { id: groupId }, data: { name } });
33
+ await tx.groupPermission.deleteMany({ where: { groupId } });
34
+ if (permissionIds && permissionIds.length > 0) {
35
+ await tx.groupPermission.createMany({
36
+ data: permissionIds.map(pid => ({ groupId, permissionId: pid })),
37
+ });
38
+ }
39
+ });
40
+ res.status(200).send();
41
+ } catch (error) { res.status(500).json({ error: 'Не удалось обновить группу' }); }
42
+ });
43
+ router.delete('/groups/:id', async (req, res) => {
44
+ try {
45
+ await prisma.group.delete({ where: { id: parseInt(req.params.id) } });
46
+ res.status(204).send();
47
+ } catch (error) { res.status(500).json({ error: 'Не удалось удалить группу' }); }
48
+ });
49
+
50
+ router.get('/all', async (req, res) => {
51
+ const permissions = await prisma.permission.findMany({ orderBy: { name: 'asc' } });
52
+ res.json(permissions);
53
+ });
54
+
55
+
56
+ router.get('/users/:username', async (req, res) => {
57
+ const user = await prisma.user.findUnique({
58
+ where: { username: req.params.username },
59
+ include: { groups: { include: { group: true } } }
60
+ });
61
+ if (!user) return res.status(404).json({ error: 'Пользователь не найден' });
62
+ res.json(user);
63
+ });
64
+
65
+ router.post('/users/:username/groups', async (req, res) => {
66
+ const { groupId } = req.body;
67
+ const user = await prisma.user.upsert({
68
+ where: { username: req.params.username },
69
+ update: {},
70
+ create: { username: req.params.username }
71
+ });
72
+ await prisma.userGroup.create({
73
+ data: { userId: user.id, groupId: parseInt(groupId) }
74
+ });
75
+ res.status(201).send();
76
+ });
77
+
78
+
79
+ module.exports = router;
@@ -0,0 +1,110 @@
1
+
2
+ const express = require('express');
3
+ const router = express.Router();
4
+ const PluginManager = require('../../core/PluginManager');
5
+
6
+ const OFFICIAL_CATALOG_URL = "https://raw.githubusercontent.com/blockmineJS/official-plugins-list/main/index.json";
7
+
8
+
9
+ const getCacheBustedUrl = (url) => `${url}?t=${new Date().getTime()}`;
10
+
11
+
12
+ router.get('/catalog', async (req, res) => {
13
+ try {
14
+ const response = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
15
+
16
+ if (!response.ok) {
17
+ const errorText = await response.text();
18
+ console.error(`[API Error] Failed to fetch catalog from GitHub. Status: ${response.status}, Response: ${errorText}`);
19
+ throw new Error(`GitHub returned status ${response.status}`);
20
+ }
21
+
22
+ res.json(await response.json());
23
+ } catch (error) {
24
+ console.error(`[API Error] Could not fetch catalog URL. Reason: ${error.message}`);
25
+ res.status(500).json({ error: 'Не удалось загрузить каталог плагинов.' });
26
+ }
27
+ });
28
+
29
+ router.post('/check-updates/:botId', async (req, res) => {
30
+ try {
31
+ const botId = parseInt(req.params.botId);
32
+
33
+ const catalogResponse = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
34
+ if (!catalogResponse.ok) throw new Error('Не удалось загрузить каталог для проверки обновлений.');
35
+ const catalog = await catalogResponse.json();
36
+
37
+ const updates = await PluginManager.checkForUpdates(botId, catalog);
38
+ res.json(updates);
39
+ } catch (error) {
40
+ console.error("[API Error] /check-updates:", error);
41
+ res.status(500).json({ error: 'Не удалось проверить обновления.' });
42
+ }
43
+ });
44
+
45
+ router.post('/update/:pluginId', async (req, res) => {
46
+ try {
47
+ const pluginId = parseInt(req.params.pluginId);
48
+ const updatedPlugin = await PluginManager.updatePlugin(pluginId);
49
+ res.json(updatedPlugin);
50
+ } catch (error) {
51
+ res.status(500).json({ error: error.message });
52
+ }
53
+ });
54
+
55
+ router.get('/catalog/:name', async (req, res) => {
56
+ try {
57
+ const pluginName = req.params.name;
58
+
59
+ const catalogResponse = await fetch(getCacheBustedUrl(OFFICIAL_CATALOG_URL));
60
+ if (!catalogResponse.ok) throw new Error(`Failed to fetch catalog, status: ${catalogResponse.status}`);
61
+
62
+ const catalog = await catalogResponse.json();
63
+ const pluginInfo = catalog.find(p => p.name === pluginName);
64
+
65
+ if (!pluginInfo) {
66
+ return res.status(404).json({ error: 'Плагин с таким именем не найден в каталоге.' });
67
+ }
68
+
69
+ let readmeContent = pluginInfo.description || 'Описание для этого плагина не предоставлено.';
70
+
71
+ try {
72
+ const urlParts = new URL(pluginInfo.repoUrl);
73
+ const pathParts = urlParts.pathname.split('/').filter(p => p);
74
+
75
+ if (pathParts.length >= 2) {
76
+ const owner = pathParts[0];
77
+ const repo = pathParts[1].replace('.git', '');
78
+
79
+ const readmeUrls = [
80
+ `https://raw.githubusercontent.com/${owner}/${repo}/main/README.md`,
81
+ `https://raw.githubusercontent.com/${owner}/${repo}/master/README.md`,
82
+ `https://raw.githubusercontent.com/${owner}/${repo}/main/readme.md`,
83
+ `https://raw.githubusercontent.com/${owner}/${repo}/master/readme.md`,
84
+ ];
85
+
86
+ for (const url of readmeUrls) {
87
+ const readmeResponse = await fetch(getCacheBustedUrl(url));
88
+ if (readmeResponse.ok) {
89
+ readmeContent = await readmeResponse.text();
90
+ break;
91
+ }
92
+ }
93
+ }
94
+ } catch (readmeError) {
95
+ console.error(`[API] Не удалось загрузить README для ${pluginName}:`, readmeError.message);
96
+ }
97
+
98
+ const finalPluginData = {
99
+ ...pluginInfo,
100
+ fullDescription: readmeContent
101
+ };
102
+
103
+ res.json(finalPluginData);
104
+ } catch (error) {
105
+ console.error(`[API Error] /catalog/:name :`, error);
106
+ res.status(500).json({ error: 'Не удалось загрузить данные плагина.' });
107
+ }
108
+ });
109
+
110
+ module.exports = router;
@@ -0,0 +1,50 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const { PrismaClient } = require('@prisma/client');
4
+ const prisma = new PrismaClient();
5
+
6
+ router.get('/', async (req, res) => {
7
+ try {
8
+ const servers = await prisma.server.findMany({ orderBy: { name: 'asc' } });
9
+ res.json(servers);
10
+ } catch (error) {
11
+ res.status(500).json({ error: 'Не удалось получить список серверов' });
12
+ }
13
+ });
14
+
15
+ router.post('/', async (req, res) => {
16
+ try {
17
+ const { name, host, port, version } = req.body;
18
+ if (!name || !host || !version) {
19
+ return res.status(400).json({ error: 'Имя, хост и версия сервера обязательны' });
20
+ }
21
+ const newServer = await prisma.server.create({
22
+ data: { name, host, port: port ? parseInt(port, 10) : 25565, version },
23
+ });
24
+ res.status(201).json(newServer);
25
+ } catch (error) {
26
+ if (error.code === 'P2002') return res.status(409).json({ error: 'Сервер с таким именем уже существует' });
27
+ res.status(500).json({ error: 'Не удалось создать сервер' });
28
+ }
29
+ });
30
+
31
+
32
+
33
+
34
+ router.delete('/:id', async (req, res) => {
35
+ try {
36
+ const serverId = parseInt(req.params.id, 10);
37
+
38
+ const botsOnServer = await prisma.bot.count({ where: { serverId } });
39
+ if (botsOnServer > 0) {
40
+ return res.status(400).json({ error: `Нельзя удалить сервер, так как он используется ${botsOnServer} ботом(ами).` });
41
+ }
42
+
43
+ await prisma.server.delete({ where: { id: serverId } });
44
+ res.status(204).send();
45
+ } catch (error) {
46
+ res.status(500).json({ error: 'Не удалось удалить сервер' });
47
+ }
48
+ });
49
+
50
+ module.exports = router;
@@ -0,0 +1,40 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const { PrismaClient } = require('@prisma/client');
4
+ const PluginService = require('../../core/PluginService');
5
+
6
+ const prisma = new PrismaClient();
7
+
8
+ router.get('/plugin-directories', async (req, res) => {
9
+ try {
10
+ const setting = await prisma.setting.findUnique({
11
+ where: { key: 'plugin_directories' },
12
+ });
13
+ res.json(setting?.value || []);
14
+ } catch (error) {
15
+ res.status(500).json({ error: 'Не удалось получить настройки директорий' });
16
+ }
17
+ });
18
+
19
+ router.post('/plugin-directories', async (req, res) => {
20
+ try {
21
+ const { directories } = req.body;
22
+ if (!Array.isArray(directories)) {
23
+ return res.status(400).json({ error: 'Ожидался массив директорий' });
24
+ }
25
+
26
+ const updatedSetting = await prisma.setting.upsert({
27
+ where: { key: 'plugin_directories' },
28
+ update: { value: directories },
29
+ create: { key: 'plugin_directories', value: directories },
30
+ });
31
+
32
+ await PluginService.syncPluginsWithDb();
33
+
34
+ res.json(updatedSetting);
35
+ } catch (error) {
36
+ res.status(500).json({ error: 'Не удалось сохранить настройки директорий' });
37
+ }
38
+ });
39
+
40
+ module.exports = router;
@@ -0,0 +1,264 @@
1
+
2
+ const { fork } = require('child_process');
3
+ const path = require('path');
4
+ const { getIO } = require('../real-time/socketHandler');
5
+ const { PrismaClient } = require('@prisma/client');
6
+ const pidusage = require('pidusage');
7
+ const DependencyService = require('./DependencyService');
8
+
9
+ const RealPermissionManager = require('./PermissionManager');
10
+ const RealUserService = require('./UserService');
11
+
12
+ const prisma = new PrismaClient();
13
+ const cooldowns = new Map();
14
+
15
+ class BotManager {
16
+ constructor() {
17
+ this.bots = new Map();
18
+ this.logCache = new Map();
19
+ this.resourceUsage = new Map();
20
+
21
+ setInterval(() => this.updateAllResourceUsage(), 5000);
22
+ }
23
+
24
+ async updateAllResourceUsage() {
25
+ if (this.bots.size === 0) {
26
+ if (this.resourceUsage.size > 0) {
27
+ this.resourceUsage.clear();
28
+ getIO().emit('bots:usage', []);
29
+ }
30
+ return;
31
+ }
32
+
33
+ const pids = Array.from(this.bots.values()).map(child => child.pid).filter(Boolean);
34
+ if (pids.length === 0) return;
35
+
36
+ try {
37
+ const stats = await pidusage(pids);
38
+ const usageData = [];
39
+
40
+ for (const pid in stats) {
41
+ if (!stats[pid]) continue;
42
+ const botId = this.getBotIdByPid(parseInt(pid, 10));
43
+ if (botId) {
44
+ const usage = {
45
+ botId: botId,
46
+ cpu: parseFloat(stats[pid].cpu.toFixed(1)),
47
+ memory: parseFloat((stats[pid].memory / 1024 / 1024).toFixed(1)),
48
+ };
49
+ this.resourceUsage.set(botId, usage);
50
+ usageData.push(usage);
51
+ }
52
+ }
53
+ getIO().emit('bots:usage', usageData);
54
+
55
+ } catch (error) {
56
+ }
57
+ }
58
+
59
+ getBotIdByPid(pid) {
60
+ for (const [botId, child] of this.bots.entries()) {
61
+ if (child.pid === pid) {
62
+ return botId;
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+
68
+ getFullState() {
69
+ const statuses = {};
70
+ this.bots.forEach((childProcess, botId) => {
71
+ if (childProcess && !childProcess.killed) {
72
+ statuses[botId] = 'running';
73
+ }
74
+ });
75
+ const logs = {};
76
+ this.logCache.forEach((logArray, botId) => {
77
+ logs[botId] = logArray;
78
+ });
79
+ return { statuses, logs };
80
+ }
81
+
82
+ emitStatusUpdate(botId, status, message = null) {
83
+ if (message) {
84
+ this.appendLog(botId, `[SYSTEM] ${message}`);
85
+ }
86
+ getIO().emit('bot:status', { botId, status, message });
87
+ }
88
+
89
+ appendLog(botId, log) {
90
+ const currentLogs = this.logCache.get(botId) || [];
91
+ const newLogs = [log, ...currentLogs.slice(0, 499)];
92
+ this.logCache.set(botId, newLogs);
93
+ getIO().emit('bot:log', { botId, log });
94
+ }
95
+
96
+ async startBot(botConfig) {
97
+ if (this.bots.has(botConfig.id)) {
98
+ const existingProcess = this.bots.get(botConfig.id);
99
+ if (existingProcess && !existingProcess.killed) {
100
+ console.error(`[BotManager] Попытка повторного запуска уже работающего бота ID: ${botConfig.id}. Запуск отменен.`);
101
+ this.appendLog(botConfig.id, `[SYSTEM-ERROR] Попытка повторного запуска. Запуск отменен.`);
102
+ return { success: false, message: 'Бот уже запущен или запускается.' };
103
+ }
104
+ }
105
+ this.logCache.set(botConfig.id, []);
106
+ this.emitStatusUpdate(botConfig.id, 'starting', '');
107
+
108
+ const allPluginsForBot = await prisma.installedPlugin.findMany({
109
+ where: { botId: botConfig.id },
110
+ });
111
+
112
+ const enabledPlugins = allPluginsForBot.filter(p => p.isEnabled);
113
+
114
+ const { sortedPlugins, pluginInfo } = DependencyService.resolveDependencies(enabledPlugins, allPluginsForBot);
115
+
116
+ let canStart = true;
117
+ if (Object.values(pluginInfo).some(info => info.issues.length > 0)) {
118
+ this.appendLog(botConfig.id, '[DependencyManager] Обнаружены проблемы с зависимостями:');
119
+ for (const plugin of Object.values(pluginInfo)) {
120
+ if (plugin.issues.length > 0) {
121
+ this.appendLog(botConfig.id, ` - Плагин "${plugin.name}":`);
122
+ for (const issue of plugin.issues) {
123
+ const logMessage = ` - [${issue.type.toUpperCase()}] ${issue.message}`;
124
+ this.appendLog(botConfig.id, logMessage);
125
+ if (['missing_dependency', 'version_mismatch', 'circular_dependency'].includes(issue.type)) {
126
+ canStart = false;
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ if (!canStart) {
134
+ this.appendLog(botConfig.id, '[DependencyManager] [FATAL] Запуск отменен из-за критических ошибок в зависимостях.');
135
+ this.emitStatusUpdate(botConfig.id, 'stopped', 'Ошибка зависимостей плагинов.');
136
+ return { success: false, message: 'Критические ошибки в зависимостях плагинов.' };
137
+ }
138
+
139
+ const fullBotConfig = { ...botConfig, plugins: sortedPlugins };
140
+
141
+ const botProcessPath = path.resolve(__dirname, 'BotProcess.js');
142
+ const child = fork(botProcessPath, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
143
+
144
+ child.on('error', (err) => {
145
+ this.appendLog(botConfig.id, `[PROCESS FATAL] КРИТИЧЕСКАЯ ОШИБКА ПРОЦЕССА: ${err.stack}`);
146
+ });
147
+
148
+ child.stderr.on('data', (data) => {
149
+ this.appendLog(botConfig.id, `[STDERR] ${data.toString()}`);
150
+ });
151
+
152
+ child.on('message', async (message) => {
153
+ if (message.type === 'log') {
154
+ this.appendLog(botConfig.id, message.content);
155
+ } else if (message.type === 'status') {
156
+ this.emitStatusUpdate(botConfig.id, message.status);
157
+ } else if (message.type === 'validate_and_run_command') {
158
+ await this.handleCommandValidation(botConfig, message);
159
+ }
160
+ });
161
+
162
+ child.on('exit', (code, signal) => {
163
+ const botId = botConfig.id;
164
+ this.bots.delete(botId);
165
+ this.resourceUsage.delete(botId);
166
+ this.emitStatusUpdate(botId, 'stopped', `Процесс завершился с кодом ${code} (сигнал: ${signal || 'none'}).`);
167
+ this.updateAllResourceUsage();
168
+ });
169
+
170
+ this.bots.set(botConfig.id, child);
171
+ child.send({ type: 'start', config: fullBotConfig });
172
+
173
+ this.appendLog(botConfig.id, '[SYSTEM] Проверка зависимостей пройдена. Запускаем процесс бота...');
174
+ return { success: true, message: 'Бот запускается' };
175
+ }
176
+
177
+ async handleCommandValidation(botConfig, message) {
178
+ const { commandName, username, args, typeChat } = message;
179
+
180
+ try {
181
+ const user = await RealUserService.getUser(username, botConfig.id, botConfig);
182
+
183
+ if (user.isBlacklisted) {
184
+ return;
185
+ }
186
+
187
+ const dbCommand = await prisma.command.findUnique({ where: { botId_name: { botId: botConfig.id, name: commandName } } });
188
+
189
+ if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner)) {
190
+ return;
191
+ }
192
+
193
+ const allowedTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
194
+ if (!allowedTypes.includes(typeChat) && !user.isOwner) {
195
+ const replyMessage = `Команду ${commandName} нельзя использовать в этом типе чата.`;
196
+ this.sendMessageToBot(botConfig.id, replyMessage, 'private', username);
197
+ return;
198
+ }
199
+
200
+ const permission = dbCommand.permissionId ? await prisma.permission.findUnique({ where: { id: dbCommand.permissionId } }) : null;
201
+ if (permission && !user.hasPermission(permission.name)) {
202
+ const replyMessage = `У вас нет прав для выполнения команды ${commandName}.`;
203
+ this.sendMessageToBot(botConfig.id, replyMessage, typeChat, username);
204
+ return;
205
+ }
206
+
207
+ const domain = (permission?.name || '').split('.')[0] || 'user';
208
+ const bypassCooldownPermission = `${domain}.cooldown.bypass`;
209
+ if (dbCommand.cooldown > 0 && !user.isOwner && !user.hasPermission(bypassCooldownPermission)) {
210
+ const cooldownKey = `${botConfig.id}:${commandName}:${user.id}`;
211
+ const now = Date.now();
212
+ const lastUsed = cooldowns.get(cooldownKey);
213
+
214
+ if (lastUsed && (now - lastUsed < dbCommand.cooldown * 1000)) {
215
+ const timeLeft = Math.ceil((dbCommand.cooldown * 1000 - (now - lastUsed)) / 1000);
216
+ const replyMessage = `Команду ${commandName} можно будет использовать через ${timeLeft} сек.`;
217
+ this.sendMessageToBot(botConfig.id, replyMessage, typeChat, username);
218
+ return;
219
+ }
220
+ cooldowns.set(cooldownKey, now);
221
+ }
222
+
223
+ const child = this.bots.get(botConfig.id);
224
+ if (child) {
225
+ child.send({ type: 'execute_handler', commandName, username, args, typeChat });
226
+ }
227
+
228
+ } catch (error) {
229
+ console.error(`[BotManager] Ошибка валидации команды ${commandName}:`, error);
230
+ const replyMessage = `Произошла внутренняя ошибка при выполнении команды.`;
231
+ this.sendMessageToBot(botConfig.id, replyMessage, 'private', username);
232
+ }
233
+ }
234
+
235
+ stopBot(botId) {
236
+ const child = this.bots.get(botId);
237
+ if (child) {
238
+ child.send({ type: 'stop' });
239
+ return { success: true };
240
+ }
241
+ return { success: false, message: 'Бот не найден или уже остановлен' };
242
+ }
243
+
244
+ sendMessageToBot(botId, message, chatType = 'command', username = null) {
245
+ const child = this.bots.get(botId);
246
+ if (child) {
247
+ // Отправляем объект с полной информацией
248
+ child.send({ type: 'chat', payload: { message, chatType, username } });
249
+ return { success: true };
250
+ }
251
+ return { success: false, message: 'Бот не найден или не запущен' };
252
+ }
253
+
254
+ invalidateUserCache(botId, username) {
255
+ RealUserService.clearCache(username, botId);
256
+ const child = this.bots.get(botId);
257
+ if (child) {
258
+ child.send({ type: 'invalidate_user_cache', username });
259
+ }
260
+ return { success: true };
261
+ }
262
+ }
263
+
264
+ module.exports = new BotManager();