2269 lines
96 KiB
JavaScript
2269 lines
96 KiB
JavaScript
// Подключаем необходимые библиотеки
|
||
const { Telegraf, Scenes, session, Markup, Stage } = require('telegraf');
|
||
const { Op } = require('sequelize');
|
||
const schedule = require('node-schedule');
|
||
const sequelize = require('./db'); // Подключение базы данных
|
||
// Подключаем обработчики
|
||
const commands = require('./commands');
|
||
const utils = require('./utils');
|
||
const handlers = require('./handlers');
|
||
const {
|
||
UserModel,
|
||
WorldModel,
|
||
JobModel,
|
||
PropertyModel,
|
||
AFKPropertyModel,
|
||
BusinessModel,
|
||
ReportModel,
|
||
BlockModel,
|
||
PromocodeModel,
|
||
EnterpriseModel,
|
||
WarehouseModel,
|
||
TruckModel,
|
||
SaleModel,
|
||
ResourcePriceModel,
|
||
SkillsModel,
|
||
DailyModel,
|
||
InviteModel,
|
||
logMiddleware,
|
||
logs
|
||
} = global.config
|
||
const bot = new Telegraf(process.env.BOT_TOKEN)
|
||
const rpg = require('./rpg'); // Подключение RPG-механик
|
||
const crime = require('./scenes/crime')
|
||
const pocketsteal = require('./scenes/pocketsteal')
|
||
const shop = require('./scenes/shop')
|
||
const pve = require('./scenes/pve')
|
||
const newpve = require('./scenes/newpve')
|
||
const stage = new Scenes.Stage([crime, pocketsteal, shop, pve, newpve]);
|
||
|
||
const start = async () => {
|
||
try {
|
||
// Подключаемся к базе данных
|
||
await sequelize.authenticate();
|
||
console.log('Подключение к базе данных успешно!');
|
||
|
||
// Синхронизация моделей, если нужно
|
||
await sequelize.sync({ alter: true });
|
||
console.log('Синхронизация моделей завершена.');
|
||
|
||
// Запуск бота
|
||
console.log('Бот успешно запущен!');
|
||
} catch (error) {
|
||
console.error('Ошибка при запуске приложения:', error);
|
||
}
|
||
}
|
||
|
||
bot.telegram.setMyCommands([{
|
||
command: "pay",
|
||
description: "Перевести указанному пользователю сумму."
|
||
},
|
||
{
|
||
command: "buy",
|
||
description: "Приобрести указанное имущество."
|
||
},
|
||
{
|
||
command: "business",
|
||
description: "Создать организацию."
|
||
},
|
||
{
|
||
command: "invite",
|
||
description: "Пригласить пользователя в организацию."
|
||
},
|
||
{
|
||
command: "percent",
|
||
description: "Установить пользователю процент заработка."
|
||
},
|
||
{
|
||
command: "report",
|
||
description: "Создать жалобу/обращение/идею."
|
||
},
|
||
{
|
||
command: "materials",
|
||
description: "Купить материалы для организации."
|
||
},
|
||
{
|
||
command: "orgmessage",
|
||
description: "Отправить сообщение в организацию."
|
||
},
|
||
{
|
||
command: "payday",
|
||
description: "Получить зарплату."
|
||
},
|
||
{
|
||
command: "enterprise",
|
||
description: "Управление предприятиями."
|
||
}
|
||
])
|
||
bot.catch((err, ctx) => {
|
||
console.error(`Произошла ошибка:`, err);
|
||
// Отправляем сообщение админу из adminList
|
||
for (let admin of global.config.adminList) {
|
||
bot.telegram.sendMessage(admin, `Произошла ошибка:\n\n${err.on.method}`);
|
||
}
|
||
switch (err.on.method) {
|
||
case 'sendMessage':
|
||
return ctx.answerCbQuery(`Произошла ошибка при отправке сообщения.`);
|
||
case 'editMessageText':
|
||
return ctx.answerCbQuery(`Произошла ошибка при редактировании сообщения.`);
|
||
default:
|
||
return ctx.answerCbQuery(`Произошла ошибка.`);
|
||
}
|
||
});
|
||
|
||
bot.use(stage.middleware())
|
||
bot.use(rpg)
|
||
|
||
bot.use(
|
||
session({
|
||
getSessionKey: (ctx) => {
|
||
if ((ctx.from && ctx.chat && ctx.chat.id === ctx.from.id) || (!ctx.chat && ctx.from)) {
|
||
return `user:${ctx.from.id}`
|
||
} else if (ctx.from && ctx.chat) {
|
||
return `${ctx.from.id}:${ctx.chat.id}`
|
||
}
|
||
return ctx.update.update_id
|
||
}
|
||
})
|
||
)
|
||
bot.use(stage)
|
||
bot.use(utils.stats)
|
||
|
||
bot.use(async (ctx, next) => {
|
||
bot.context.config = require('./ctxconfig.json')
|
||
let id = ctx.from.id
|
||
let username = ctx.from.username
|
||
global.messagesCounter.inc({ type: ctx.updateType });
|
||
if (username == null) username = ctx.from.id
|
||
switch (ctx.updateType) {
|
||
case `message`:
|
||
console.log(utils.getCurrentTime() + `: ` + username + `: ` + ctx.update.message.text)
|
||
break;
|
||
case `callback_query`:
|
||
console.log(utils.getCurrentTime() + `: ${username}: ${ctx.update.callback_query.data}`)
|
||
break;
|
||
default:
|
||
console.log(ctx)
|
||
}
|
||
let user = await UserModel.findByPk(id);
|
||
let block = await BlockModel.findOne({
|
||
where: { telegram_id: id }, // Здесь id — это значение telegram_id, переданное в функцию
|
||
});
|
||
let property = await PropertyModel.findByPk(id);
|
||
let skill = await SkillsModel.findByPk(ctx.from.id)
|
||
if (!user) ctx.reply(`❕ Первичная регистрация профиля.`);
|
||
if (user === null) {
|
||
await UserModel.create({
|
||
telegram_id: id,
|
||
username: username,
|
||
name: ctx.from.first_name
|
||
})
|
||
} else {
|
||
user.name = ctx.from.first_name
|
||
if (user.username === null) user.username = ctx.from.id
|
||
user.save()
|
||
}
|
||
if (property === null) {
|
||
await PropertyModel.create({
|
||
telegram_id: id
|
||
})
|
||
}
|
||
//if (whitelist.includes(id) == false) return ctx.reply(`У вас пока нет доступа к боту. Следите за обновлениями в группе: t.me/CampFireGameBotNews`)
|
||
if (block) {
|
||
const currentTime = Math.trunc(Date.now() / 1000); // Получаем текущее время в секундах
|
||
|
||
if (block.isBlocked && block.time > currentTime) {
|
||
const remainingTime = Math.trunc((block.time - currentTime) / 60); // Рассчитываем оставшееся время
|
||
return ctx.reply(
|
||
`📛 У вас активная блокировка по причине: ${block.reason}.\n⏲️ Оставшееся время: ${remainingTime} мин.`
|
||
);
|
||
}
|
||
|
||
// Если блокировка истекла или отключена
|
||
block.isBlocked = false;
|
||
await block.save(); // Обязательно используем `await` для сохранения изменений
|
||
}
|
||
|
||
const start = Date.now()
|
||
const timeoutPromise = new Promise((resolve, reject) => {
|
||
|
||
setTimeout(() => {
|
||
reject(new Error('timeout'))
|
||
}, 1000 * 5)
|
||
})
|
||
|
||
const nextPromise = next()
|
||
.then(() => {
|
||
const ms = Date.now() - start
|
||
})
|
||
.catch((error) => {
|
||
handleError(error, ctx)
|
||
})
|
||
|
||
return Promise.race([timeoutPromise, nextPromise])
|
||
.catch((error) => {
|
||
global.errorsCounter.inc(); // Увеличиваем счётчик ошибок
|
||
if (error.message === 'timeout') {
|
||
console.error('timeout', ctx.update)
|
||
return false
|
||
}
|
||
global.errorsCounter.inc(); // Увеличиваем счётчик ошибок
|
||
return true
|
||
})
|
||
|
||
})
|
||
|
||
bot.command('start', async (ctx) => {
|
||
try {
|
||
if (ctx.payload) { // Система обработки приглашений в зависимости от payload
|
||
const invite = await InviteModel.findOne({ where: { uid: ctx.payload } });
|
||
|
||
if (invite) {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (!user) {
|
||
return await ctx.reply('Ваш профиль не найден.');
|
||
}
|
||
|
||
switch (invite.type) {
|
||
case 'organization': {
|
||
const business = await BusinessModel.findByPk(invite.value);
|
||
|
||
if (business) {
|
||
// Проверка, состоит ли пользователь уже в организации
|
||
if (user.business.id != 0) {
|
||
return await ctx.reply('Вы уже состоите в организации.');
|
||
}
|
||
|
||
user.business = {
|
||
id: business.owner,
|
||
checks: 0,
|
||
percent: 0
|
||
};
|
||
await user.save({ fields: ['business'] });
|
||
|
||
// Добавляем пользователя в список организации
|
||
business.users = sequelize.fn('array_append', sequelize.col('users'), ctx.from.id);
|
||
|
||
await business.save();
|
||
|
||
return await ctx.reply(`Вы вступили в организацию "${business.name}".`);
|
||
} else {
|
||
return await ctx.reply('Организация не найдена.');
|
||
}
|
||
}
|
||
|
||
case 'referral': {
|
||
const inviter = await UserModel.findByPk(invite.author);
|
||
|
||
if (ctx.from.id === invite.author) {
|
||
return await ctx.reply('Вы не можете пригласить самого себя.');
|
||
}
|
||
|
||
if (user.referated) {
|
||
return await ctx.reply('Вы уже воспользовались приглашением.');
|
||
}
|
||
|
||
if (inviter) {
|
||
inviter.money += 250000;
|
||
|
||
|
||
user.money += 250000;
|
||
user.referated = true;
|
||
|
||
// Управление статусами
|
||
if (inviter.status === 'bronze') {
|
||
inviter.statustime += 4320
|
||
await ctx.reply('Ваш статус "Bronze" продлен на 3 дня.');
|
||
} else if (inviter.status === 'user') {
|
||
inviter.status = 'bronze';
|
||
inviter.statustime = 4320
|
||
await ctx.reply('Вам выдан статус "Bronze" на 3 дня.');
|
||
}
|
||
await user.save();
|
||
await inviter.save();
|
||
|
||
// Обновление данных приглашения
|
||
invite.users.push(ctx.from.id);
|
||
invite.value++;
|
||
await invite.save();
|
||
|
||
await ctx.reply(`Вы получили бонус от пользователя ${inviter.username}.`);
|
||
await ctx.reply(`Пользователь ${inviter.username} получил статус "Bronze" на 3 дня.`);
|
||
}
|
||
break;
|
||
}
|
||
|
||
default:
|
||
return await ctx.reply('Неверный тип приглашения.');
|
||
}
|
||
} else {
|
||
return await ctx.reply('Приглашение не найдено.');
|
||
}
|
||
}
|
||
|
||
// Главное меню
|
||
return await ctx.reply(
|
||
'Главное меню',
|
||
Markup.keyboard([
|
||
['😎 Профиль'], // Row1
|
||
['🗄️ Работать', '🌐 Организация', '🏗️ Предприятия', '🏯 Казино'], // Row2
|
||
['CampFireGG.Crime'], // Row3
|
||
['🎁 Бонус'], // Row4
|
||
['📦 Контейнеры'], // Row5
|
||
['📢 Вакансии', '🔵 Имущество', '📞 Пригласить'], // Row6
|
||
['🛡️ Test'], // Row7
|
||
]).resize()
|
||
);
|
||
} catch (error) {
|
||
console.error('Ошибка в команде start:', error);
|
||
await ctx.reply('Произошла ошибка. Пожалуйста, попробуйте позже.');
|
||
}
|
||
});
|
||
|
||
|
||
bot.hears('🛡️ Test', async (ctx) => {
|
||
const buttons = [
|
||
[Markup.button.callback('PVE', 'locations')],
|
||
];
|
||
ctx.reply(`🚨 Добро пожаловать в тестовый режим 🚨\nВесь функционал в этом режиме еще не сбалансирован\n`, Markup.inlineKeyboard(buttons));
|
||
})
|
||
|
||
bot.command('profile', (ctx) => {
|
||
return ctx.reply('👤 Ваш профиль:', {
|
||
reply_markup: {
|
||
inline_keyboard: [
|
||
[
|
||
{
|
||
text: "Открыть профиль",
|
||
web_app: { url: "https://web-bot.campfiregg.ru/" }, // Замените на ваш URL
|
||
},
|
||
],
|
||
],
|
||
},
|
||
});
|
||
});
|
||
|
||
bot.command('link', async (ctx) => {
|
||
try {
|
||
// Шаг 1: Получаем пользователя из базы данных
|
||
let user = await UserModel.findByPk(ctx.from.id);
|
||
|
||
if (!user) {
|
||
return ctx.reply("Пользователь не найден в базе данных.");
|
||
}
|
||
|
||
// Шаг 2: Получаем список пользователей на сервере
|
||
const response = await fetch('https://staff.campfiregg.ru/api/users', {
|
||
method: 'GET',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': 'Token tknCaMpFiReApIStAfFend'
|
||
}
|
||
});
|
||
|
||
const users = await response.json();
|
||
|
||
// Шаг 3: Ищем пользователя, у которого в "bio" содержится ctx.from.id (ID бота)
|
||
const staffuser = users.data.find(staff => staff.attributes.bio === String(ctx.from.id));
|
||
|
||
if (!staffuser) {
|
||
// Если пользователя не нашли
|
||
return ctx.reply("Пользователь не найден. Пожалуйста, установите ваш ID в описание профиля и повторите попытку.");
|
||
} else {
|
||
// Шаг 4: Если нашли, добавляем staffID в базу данных бота
|
||
const staffUserId = staffuser.id; // ID пользователя на сервере (staff)
|
||
user.staffID = staffUserId;
|
||
|
||
await user.save(); // Сохраняем обновления в базе данных
|
||
|
||
// Ответ пользователю
|
||
return ctx.reply('Профиль успешно привязан');
|
||
}
|
||
|
||
} catch (err) {
|
||
// Обработка ошибок
|
||
console.error(err);
|
||
return ctx.reply("Произошла ошибка. Попробуйте снова.");
|
||
}
|
||
});
|
||
|
||
|
||
|
||
|
||
bot.hears('промка', async (ctx) => {
|
||
generatePromo()
|
||
});
|
||
|
||
bot.hears('▶️ Меню', commands.menu);
|
||
|
||
bot.hears('Чат', async (ctx) => {
|
||
ctx.reply(`${ctx.message.chat.id}`)
|
||
});
|
||
|
||
bot.hears('😎 Профиль', commands.profile);
|
||
|
||
bot.command('getprofile', commands.getprofile);
|
||
|
||
bot.hears('Рандом', async (ctx) => {
|
||
let users = await UserModel.findAll();
|
||
let chosenOne = users.random()
|
||
return ctx.reply(`
|
||
👤 ${chosenOne.username}
|
||
🆔: ${chosenOne.telegram_id}
|
||
`);
|
||
|
||
});
|
||
|
||
bot.hears('💳 Баланс', async (ctx) => {
|
||
let user = await UserModel.findByPk(ctx.from.id);
|
||
return ctx.reply(`
|
||
⏩ Аккаунт игрока ${user.username}
|
||
🆔 Игрока: ${user.telegram_id}
|
||
|
||
📶 Уровень: ${user.level}
|
||
⏩ Повышается за различные действия.
|
||
|
||
💰 Баланс:
|
||
¤${user.money}
|
||
`);
|
||
})
|
||
|
||
bot.hears('🎁 Бонус', commands.bonus)
|
||
|
||
bot.hears('Гараж', commands.garage)
|
||
|
||
bot.hears('Гонка', commands.race)
|
||
|
||
bot.command('pay', commands.pay)
|
||
|
||
bot.hears('Мир', commands.worldMenu)
|
||
|
||
bot.hears('📞 Пригласить', commands.referal)
|
||
|
||
bot.hears('📢 Вакансии', commands.jobs)
|
||
|
||
bot.action(/job_(1|2|3|4|5|6|7|leave)/, commands.chooseJob)
|
||
|
||
bot.hears('🏯 Казино', commands.casinoMenu)
|
||
|
||
bot.hears('🗄️ Работать', commands.work)
|
||
|
||
bot.hears('Топ', commands.top)
|
||
|
||
bot.hears('🔵 Имущество', commands.propertyMenu)
|
||
|
||
bot.action('shopmenu', commands.propertyMenu)
|
||
|
||
bot.action(/shop_(house|phone|car)/, commands.propertyList)
|
||
|
||
bot.action(/{"action": "buy"*/, commands.propertyBuy)
|
||
|
||
bot.hears('Поставщик', commands.hatkeisList)
|
||
|
||
bot.command('sell', commands.propertySell)
|
||
|
||
bot.hears('🌐 Организация', commands.organizationMenu)
|
||
|
||
bot.action('workoff', commands.workoff)
|
||
|
||
bot.action('покинуть', commands.leaveOrg)
|
||
|
||
bot.action('cancel', async (ctx) => {
|
||
await ctx.deleteMessage()
|
||
await bot.telegram.answerCbQuery(ctx.callbackQuery.id, `Отмена.`)
|
||
})
|
||
|
||
bot.action('user_leave_business', commands.leaveOrgAccept)
|
||
|
||
bot.action('payday', commands.payday)
|
||
|
||
bot.command('orgmessage', commands.orgMessage)
|
||
|
||
bot.command('materials', commands.materialsBuy)
|
||
|
||
bot.command('percent', commands.percentSet)
|
||
|
||
bot.command('business', commands.organizationCreate)
|
||
|
||
bot.command('invite', commands.invite)
|
||
|
||
bot.action(/{"type": "business_invite_(accept|refuse)"*/, commands.inviteAction)
|
||
|
||
bot.command('report', commands.report)
|
||
|
||
bot.hears('🎰 Слоты', commands.slotsMenu)
|
||
// bot.action(/slots(1000|5000|25000|50000|100000)/, commands.slotsRun)
|
||
|
||
bot.hears('Помощь', async (ctx) => {
|
||
return await ctx.replyWithMarkdownV2(`• [Функционал](https://telegra.ph/CampFire-Bot-Info-09-25)\n• [Правила](https://telegra.ph/PRAVILA-BOTA-09-25)`, {
|
||
disable_web_page_preview: true
|
||
})
|
||
})
|
||
|
||
bot.command('promocode', commands.promocodeActivate)
|
||
|
||
bot.hears('📦 Контейнеры', commands.carContainers)
|
||
|
||
bot.action(/container_(1|2|3|4|5|6|7|8|9|10)/, commands.chooseContainer)
|
||
|
||
bot.action(/{"action": "sueta_*/, async (ctx) => {
|
||
let data = ctx.update.callback_query.data;
|
||
data = JSON.parse(data)
|
||
console.log(data.car)
|
||
let user = await UserModel.findByPk(ctx.from.id)
|
||
let property = await PropertyModel.findByPk(ctx.from.id)
|
||
switch (data.action) {
|
||
case `sueta_accept`:
|
||
user.money += data.carprice
|
||
await ctx.editMessageText(`➕ ${data.carprice}`)
|
||
break;
|
||
case `sueta_refuse`:
|
||
user.money += Math.round(property.car.price / 2)
|
||
property.car = {
|
||
name: data.carname,
|
||
price: data.carprice
|
||
}
|
||
await ctx.editMessageText(`➕ ${data.carname}`)
|
||
break;
|
||
}
|
||
user.save()
|
||
property.save()
|
||
})
|
||
|
||
bot.action(/dissolve_organization_(\d+)/, async (ctx) => {
|
||
try {
|
||
const businessId = parseInt(ctx.match[1], 10);
|
||
const business = await BusinessModel.findByPk(businessId);
|
||
if (!business) {
|
||
return ctx.answerCbQuery('Организация не найдена.', { show_alert: true });
|
||
}
|
||
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
|
||
if (business.owner != ctx.from.id) {
|
||
return ctx.answerCbQuery('Только владелец может ликвидировать организацию.', { show_alert: true });
|
||
}
|
||
|
||
// Расчет и передача 25% от баланса владельцу
|
||
const payout = Math.floor(business.balance * 0.25);
|
||
user.money += payout;
|
||
user.business = { id: 0, checks: 0, percent: 0 };
|
||
await user.save();
|
||
|
||
// Сброс бизнес-данных у участников
|
||
const members = await UserModel.findAll({
|
||
where: {
|
||
'business.id': businessId,
|
||
},
|
||
});
|
||
|
||
for (const member of members) {
|
||
member.business = { id: 0, checks: 0, percent: 0 };
|
||
await member.save();
|
||
}
|
||
logs(ctx, `Ликвидация организации ${business.name}`, { businessId, payout });
|
||
// Удаление организации
|
||
await business.destroy();
|
||
|
||
await ctx.reply(`❌ Организация "${business.name}" ликвидирована. Вы получили ₽${spaces(payout)}.`);
|
||
} catch (error) {
|
||
console.error('Ошибка при ликвидации организации:', error);
|
||
return ctx.reply('Произошла ошибка. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
bot.command('daily_bonus', async (ctx) => {
|
||
try {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
const currentDate = new Date();
|
||
const day = currentDate.getDate();
|
||
|
||
// Проверяем, заходил ли пользователь сегодня
|
||
const lastLoginDate = new Date(user.lastLogin);
|
||
if (
|
||
lastLoginDate.getFullYear() === currentDate.getFullYear() &&
|
||
lastLoginDate.getMonth() === currentDate.getMonth() &&
|
||
lastLoginDate.getDate() === day
|
||
) {
|
||
return ctx.reply('Вы уже получили награду за сегодня! Возвращайтесь завтра.');
|
||
}
|
||
|
||
// Получение награды из базы
|
||
const reward = await DailyModel.findOne({ where: { day } });
|
||
if (!reward) {
|
||
return ctx.reply('На сегодня награды не настроены. Обратитесь к администратору.');
|
||
}
|
||
|
||
// Обработка награды
|
||
let totalMoney = reward.money;
|
||
let message = `🎉 Награда за ${day} число:\n`;
|
||
|
||
if (reward.experience > 0) {
|
||
user.exp += reward.experience;
|
||
message += `✨ Опыт: ${reward.experience}\n`;
|
||
}
|
||
|
||
if (user.business.id > 0) {
|
||
user.materials += reward.materials;
|
||
message += `🏗 Материалы: ${reward.materials}\n`;
|
||
} else {
|
||
totalMoney += reward.materials; // Материалы заменяются на деньги
|
||
}
|
||
|
||
// Добавляем деньги
|
||
user.money += totalMoney;
|
||
message += `💰 Деньги: ${totalMoney}\n`;
|
||
|
||
// Обновляем прогресс
|
||
//user.lastLogin = currentDate;
|
||
user.loginStreak = (lastLoginDate.getDate() === day - 1) ? user.loginStreak + 1 : 1;
|
||
await user.save();
|
||
|
||
// Отправляем сообщение
|
||
await ctx.reply(`${message}\n🔥 Ваша серия входов: ${user.loginStreak} дней.`);
|
||
} catch (error) {
|
||
console.error('Ошибка при выдаче ежедневного бонуса:', error);
|
||
return ctx.reply('Произошла ошибка. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
bot.command('daily', async (ctx) => {
|
||
try {
|
||
// Получаем данные пользователя
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
|
||
// Создаем клавиатуру с кнопкой перехода в мини-приложение
|
||
const keyboard = {
|
||
inline_keyboard: [
|
||
[
|
||
{
|
||
text: '🎁 Ежедневный бонус',
|
||
web_app: { url: 'https://web-bot.campfiregg.ru/daily' } // Замените на URL мини-приложения
|
||
}
|
||
]
|
||
]
|
||
};
|
||
|
||
// Отправляем сообщение с кнопкой
|
||
await ctx.reply('👋 Добро пожаловать в раздел ежедневных наград! Нажмите на кнопку ниже, чтобы открыть календарь и получить сегодняшнюю награду.', {
|
||
reply_markup: keyboard
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка при открытии ежедневного бонуса:', error);
|
||
return ctx.reply('Произошла ошибка. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
bot.action('manage_organization', async (ctx) => {
|
||
try {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (user.business.id === 0) {
|
||
return await ctx.reply('Вы не состоите в организации.');
|
||
}
|
||
|
||
const business = await BusinessModel.findOne({ where: { owner: ctx.from.id.toString() } });
|
||
if (!business) {
|
||
return await ctx.reply('Организация не найдена.');
|
||
}
|
||
if (business.owner != ctx.from.id) {
|
||
return await ctx.reply('Вы не являетесь владельцем организации.');
|
||
}
|
||
|
||
const buttons = [
|
||
[{ text: '👥 Участники', callback_data: 'org_members' }],
|
||
[{ text: '📞 Пригласить', callback_data: 'org_invite' }],
|
||
[{ text: '🚫 Расформировать', callback_data: `dissolve_organization_${business.id}` }],
|
||
[{ text: '⬅️ Назад', callback_data: 'organization_menu' }]
|
||
];
|
||
|
||
return await ctx.editMessageText(`🏭 Управление организацией "${business.name}":`, Markup.inlineKeyboard(buttons).resize());
|
||
} catch (error) {
|
||
console.error('Ошибка при открытии меню управления организацией:', error);
|
||
return await ctx.reply('Произошла ошибка. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
bot.action('org_members', async (ctx) => {
|
||
try {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (user.business.id === 0) {
|
||
return await ctx.reply('Вы не состоите в организации.');
|
||
}
|
||
|
||
const business = await BusinessModel.findOne({ where: { owner: ctx.from.id.toString() } });
|
||
if (!business) {
|
||
return await ctx.reply('Организация не найдена.');
|
||
}
|
||
|
||
if (business.owner != ctx.from.id) {
|
||
return await ctx.reply('Вы не являетесь владельцем организации.');
|
||
}
|
||
const buttons = [];
|
||
let message = `👥 Участники организации "${business.name}":\n\n`;
|
||
for (const member of business.users) {
|
||
const user = await UserModel.findByPk(member);
|
||
buttons.push([{ text: `👤 ${user.username}`, callback_data: `org_view_member_${user.telegram_id}` }]);
|
||
}
|
||
|
||
buttons.push([{ text: '🔧 Назад', callback_data: 'manage_organization' }]);
|
||
|
||
return await ctx.editMessageText(message, Markup.inlineKeyboard(buttons).resize());
|
||
} catch (error) {
|
||
console.error('Ошибка при просмотре участников организации:', error);
|
||
return await ctx.reply('Произошла ошибка. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
bot.action(/org_view_member_(\d+)/, async (ctx) => {
|
||
try {
|
||
const userId = parseInt(ctx.match[1], 10);
|
||
const user = await UserModel.findByPk(userId);
|
||
if (!user) {
|
||
return await ctx.reply('Пользователь не найден.');
|
||
}
|
||
|
||
const business = await BusinessModel.findOne({ where: { owner: ctx.from.id } });
|
||
if (!business) {
|
||
return await ctx.reply('Вы не являетесь владельцем организации.');
|
||
}
|
||
|
||
if (user.business.id != business.owner) {
|
||
return await ctx.reply('Пользователь не состоит в вашей организации.');
|
||
}
|
||
|
||
const buttons = [
|
||
[{ text: '📉 Уволить', callback_data: `fireuser_${user.telegram_id}` }],
|
||
[{ text: '🔧 Назад', callback_data: 'org_members' }]
|
||
];
|
||
|
||
// Рассчитываем эффективность пользователя
|
||
const userChecks = user.business.checks;
|
||
const businessChecks = business.checks;
|
||
const userEfficiency = userChecks > 0 ? Math.floor((userChecks / businessChecks) * 100) : 0;
|
||
|
||
const message = `👤 Профиль участника "${user.username}":\n\n` +
|
||
`📶 Отработки: ${user.business.checks}\n` +
|
||
`💰 Процент: ${user.business.percent}%\n` +
|
||
`🔥 Эффективность: ${userEfficiency}%\n`;
|
||
|
||
return await ctx.editMessageText(message, Markup.inlineKeyboard(buttons).resize());
|
||
} catch (error) {
|
||
console.error('Ошибка при просмотре участника организации:', error);
|
||
return await ctx.reply('Произошла ошибка. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
// Команда для увольнения сотрудника из организации
|
||
bot.action(/fireuser_(\d+)/, async (ctx) => {
|
||
try {
|
||
const userId = parseInt(ctx.match[1], 10);
|
||
const user = await UserModel.findByPk(userId);
|
||
if (!user) {
|
||
return await ctx.reply('Пользователь не найден.');
|
||
}
|
||
|
||
const business = await BusinessModel.findOne({ where: { owner: ctx.from.id } });
|
||
if (!business) {
|
||
return await ctx.reply('Вы не являетесь владельцем организации.');
|
||
}
|
||
|
||
if (user.business.id != business.owner) {
|
||
return await ctx.reply('Пользователь не состоит в вашей организации.');
|
||
}
|
||
|
||
if (user.telegram_id == business.owner) {
|
||
return await ctx.reply('Вы не можете уволить себя.');
|
||
}
|
||
|
||
// Удаляем пользователя из массива участников\
|
||
business.users = business.users.filter((id) => id !== user.telegram_id);
|
||
await business.save();
|
||
|
||
user.business = { id: 0, checks: 0, percent: 0 };
|
||
await user.save();
|
||
|
||
return await ctx.reply(`Пользователь ${user.username} уволен из организации.`);
|
||
} catch (error) {
|
||
console.error('Ошибка при увольнении сотрудника:', error);
|
||
return await ctx.reply('Произошла ошибка. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
bot.action('org_invite', async (ctx) => {
|
||
try {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (user.business.id === 0) {
|
||
return await ctx.reply('Вы не состоите в организации.');
|
||
}
|
||
|
||
const business = await BusinessModel.findOne({ where: { owner: ctx.from.id.toString() } });
|
||
if (!business) {
|
||
return await ctx.reply('Организация не найдена.');
|
||
}
|
||
|
||
if (business.owner != ctx.from.id) {
|
||
return await ctx.reply('Вы не являетесь владельцем организации.');
|
||
}
|
||
|
||
// Создаем приглашение
|
||
const invites = await InviteModel.findAll({ where: { type: 'organization', author: ctx.from.id } });
|
||
if (invites.length > 0) {
|
||
const invite = invites[0];
|
||
const inviteLink = `https://t.me/${process.env.BOT_USERNAME}?start=${invite.uid}`;
|
||
return await ctx.reply(`📞 Приглашение для организации "${business.name}":\n\n${inviteLink}`);
|
||
}
|
||
const invite = await InviteModel.create({ type: 'organization', uid: `${user.username}_org_invite` , author: ctx.from.id, value: business.id });
|
||
const inviteLink = `https://t.me/${process.env.BOT_USERNAME}?start=${invite.uid}`;
|
||
|
||
return await ctx.reply(`📞 Приглашение для организации "${business.name}":\n\n${inviteLink}`);
|
||
} catch (error) {
|
||
console.error('Ошибка при создании приглашения в организацию:', error);
|
||
return await ctx.reply('Произошла ошибка. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
/////////////////////////////////////Enterprise Update 20.12.2024//////////////////////////////////////////////////
|
||
|
||
// Обновление меню "Предприятия"
|
||
bot.hears('🏗️ Предприятия', async (ctx) => {
|
||
try {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (!user) {
|
||
console.error('Ошибка: пользователь не найден');
|
||
return await ctx.reply('Ошибка: пользователь не найден.');
|
||
}
|
||
const warehouse = await WarehouseModel.findOne({ where: { playerId: user.telegram_id } });
|
||
const enterprises = await EnterpriseModel.findAll({
|
||
where: { playerId: user.telegram_id }
|
||
});
|
||
let message = `🏗️ Меню предприятий:\n`;
|
||
message += `У вас: ${enterprises.length} предприятий.\n`;
|
||
message += warehouse
|
||
? `🗄️ Единый склад: Доступен (Ёмкость: ${warehouse.capacity} ед.)\n`
|
||
: `🗄️ Единый склад: Не построен.\n`;
|
||
|
||
const buttons = [];
|
||
buttons.push([{ text: '🏭 Мои предприятия', callback_data: 'my_enterprises' }]);
|
||
buttons.push([{ text: '🛠️ Построить предприятие', callback_data: 'build_enterprise' }]);
|
||
|
||
if (warehouse) {
|
||
buttons.push([{ text: '🚛 Управление складом', callback_data: 'manage_warehouse' }]);
|
||
} else {
|
||
buttons.push([{ text: '🗄️ Построить склад', callback_data: 'buy_warehouse' }]);
|
||
}
|
||
|
||
buttons.push([{ text: '💰 Продать ресурсы', callback_data: 'sell_resources' }]);
|
||
|
||
return await ctx.reply(message, Markup.inlineKeyboard(buttons).resize());
|
||
} catch (error) {
|
||
console.error('Ошибка при выполнении команды "🏗️ Предприятия":', error);
|
||
return await ctx.reply('Произошла ошибка при выполнении команды. Попробуйте позже.');
|
||
}
|
||
});
|
||
|
||
bot.action('enterprise_menu', async (ctx) => {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
const warehouse = await WarehouseModel.findOne({ where: { playerId: user.telegram_id } });
|
||
|
||
const enterprises = await EnterpriseModel.findAll({
|
||
where: { playerId: user.telegram_id }
|
||
});
|
||
|
||
let message = `🏗️ Меню предприятий:\n`;
|
||
message += `У вас: ${enterprises.length} предприятий.\n`;
|
||
message += warehouse
|
||
? `🗄️ Единый склад: Доступен (Ёмкость: ${warehouse.capacity} ед.)\n`
|
||
: `🗄️ Единый склад: Не построен.\n`;
|
||
|
||
const buttons = [];
|
||
|
||
// Добавляем кнопки для предприятий
|
||
buttons.push([{ text: '🏭 Мои предприятия', callback_data: 'my_enterprises' }]);
|
||
buttons.push([{ text: '🛠️ Построить предприятие', callback_data: 'build_enterprise' }]);
|
||
|
||
// Кнопка для управления или постройки склада
|
||
if (warehouse) {
|
||
buttons.push([{ text: '🚛 Управление складом', callback_data: 'manage_warehouse' }]);
|
||
} else {
|
||
buttons.push([{ text: '🗄️ Построить склад', callback_data: 'buy_warehouse' }]);
|
||
}
|
||
buttons.push([{ text: '💰 Продать ресурсы', callback_data: 'sell_resources' }])
|
||
// Возвращаем меню с кнопками
|
||
return await ctx.editMessageText(message, Markup.inlineKeyboard(buttons).resize());
|
||
});
|
||
|
||
// Функция для преобразования типа ресурса в эмодзи
|
||
function getEnterpriseEmoji(resourceType) {
|
||
const emojis = {
|
||
wood: '🌳',
|
||
coal: '⛏️',
|
||
oil: '🛢️',
|
||
metall: '⚒️',
|
||
gold: '💰',
|
||
diamond: '💎'
|
||
}
|
||
return emojis[resourceType] || '🏭'
|
||
}
|
||
|
||
bot.action('my_enterprises', async (ctx) => {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
const enterprises = await EnterpriseModel.findAll({
|
||
where: { playerId: user.telegram_id }
|
||
});
|
||
|
||
if (enterprises.length === 0) {
|
||
return await ctx.reply('У вас нет предприятий.');
|
||
}
|
||
let metallPrice = await ResourcePriceModel.findOne({
|
||
where: { resource: "metall" }
|
||
});
|
||
let message = `🏭 Мои предприятия:\n\n`;
|
||
const buttons = [];
|
||
|
||
for (const enterprise of enterprises) {
|
||
const { id, name, level, efficiency, currentResources, resourceType, warehouseCapacity } = enterprise;
|
||
const resourcePrice = await ResourcePriceModel.findOne({
|
||
where: { resource: resourceType }
|
||
});
|
||
let price = resourcePrice.price * 1.5 * 10 * 24 * 7 // Стоимость улучшения зависит от уровня
|
||
message += `🔹 [ID: ${id}] ${getEnterpriseEmoji(resourceType)} ${name} st. ${level}\n └── ${currentResources}/${warehouseCapacity} [${efficiency} ед/ч]\n └──📈 ${utils.spaces(price)} руб.\n\n`;
|
||
const truck = await TruckModel.findOne({ where: { enterpriseId: id } });
|
||
const warehouse = await WarehouseModel.findOne({ where: { playerId: user.telegram_id } });
|
||
|
||
const enterpriseButtons = [
|
||
{ text: `ID: 📈 ${id}`, callback_data: `upgrade_${id}` },
|
||
];
|
||
|
||
if (!truck) {
|
||
enterpriseButtons.push({ text: `🛻 ID: ${id}`, callback_data: `hire_truck_${id}` });
|
||
} else {
|
||
enterpriseButtons.push({ text: `🚛 ID: ${id}`, callback_data: `upgrade_truck_${id}` });
|
||
}
|
||
|
||
enterpriseButtons.push({ text: `💰 ID: ${id}`, callback_data: `sell_all_${id}` });
|
||
|
||
if (warehouse) {
|
||
enterpriseButtons.push({ text: `🔄 ID: ${id}`, callback_data: `transfer_from_${id}` });
|
||
}
|
||
enterpriseButtons.push({ text: `🚫 ID: ${id}`, callback_data: `sell_enterprise_${id}` });
|
||
buttons.push(enterpriseButtons);
|
||
}
|
||
message += `\n\n📈 - Улучшить\n🛻 - Купить грузовик [~${utils.spaces(metallPrice.price * 700)} руб.]\n🚛 - Улучшить грузовик\n💰 - Продать ресурсы с предприятия\n🔄 - Перевезти все на склад`
|
||
buttons.push([{ text: '⬅️ Назад', callback_data: 'enterprise_menu' }]);
|
||
|
||
return await ctx.editMessageText(message, Markup.inlineKeyboard(buttons).resize());
|
||
});
|
||
|
||
// Построение предприятия
|
||
bot.action('build_enterprise', async (ctx) => {
|
||
try {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (!user) {
|
||
return await ctx.reply('Пользователь не найден.');
|
||
}
|
||
|
||
const enterprises = await EnterpriseModel.findAll({
|
||
where: { playerId: user.telegram_id }
|
||
});
|
||
|
||
if (enterprises.length >= 5) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, 'Вы достигли максимального числа предприятий.', { show_alert: true });
|
||
}
|
||
|
||
// Получаем текущие цены из базы данных
|
||
const resourcePrices = await ResourcePriceModel.findAll();
|
||
if (!resourcePrices || resourcePrices.length === 0) {
|
||
return await ctx.reply('Цены на ресурсы отсутствуют. Пожалуйста, попробуйте позже.');
|
||
}
|
||
|
||
let message = `🛠️ Постройка предприятия:\n`;
|
||
const buttons = [];
|
||
|
||
for (const resource of resourcePrices) {
|
||
const resourceName = getReadableType(resource.resource);
|
||
const resourceEmoji = getEnterpriseEmoji(resource.resource);
|
||
message += `${resourceEmoji} ${resourceName}: ${utils.spaces(Math.round(resource.price * 1.5 * 10 * 24 * 7))} руб.\n`;
|
||
|
||
// Генерируем кнопки для каждого ресурса
|
||
buttons.push([
|
||
{ text: resourceEmoji, callback_data: `build_${resource.resource}` }
|
||
]);
|
||
}
|
||
|
||
message += '\nВыберите тип предприятия:';
|
||
buttons.push([{ text: '⬅️ Назад', callback_data: 'enterprise_menu' }]);
|
||
return await ctx.editMessageText(message, Markup.inlineKeyboard(buttons).resize());
|
||
} catch (error) {
|
||
console.error('Ошибка в build_enterprise:', error);
|
||
return await ctx.reply('Произошла ошибка при обработке запроса. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
// Построение предприятия по выбранному типу
|
||
bot.action(/build_(wood|coal|oil|metall|gold|diamond)/, async (ctx) => {
|
||
let user = await UserModel.findByPk(ctx.from.id)
|
||
let data = ctx.update.callback_query.data
|
||
let type = data.split('_')[1]
|
||
|
||
|
||
// Получаем текущие цены из базы данных
|
||
const resourcePrice = await ResourcePriceModel.findOne({
|
||
where: { resource: type }
|
||
});
|
||
let price = resourcePrice.price * 1.5 * 10 * 24 * 7
|
||
|
||
// Проверка, есть ли достаточно денег у пользователя
|
||
if (user.money < price) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `У вас недостаточно средств для постройки предприятия. Необходимо ${utils.spaces(price)} руб.`, { show_alert: true })
|
||
}
|
||
// Проверка уровня, для покупки предприятия, для покупки деревообрабатывающего предприятия нужен 5 уровень и каждое следующее предприятие требует уровень выше
|
||
switch (type) {
|
||
case 'wood':
|
||
if (user.level < 5) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `Для постройки деревообрабатывающего предприятия необходим 5 уровень.`, { show_alert: true })
|
||
}
|
||
break;
|
||
case 'coal':
|
||
if (user.level < 6) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `Для постройки угольного предприятия необходим 10 уровень.`, { show_alert: true })
|
||
}
|
||
break;
|
||
case 'oil':
|
||
if (user.level < 7) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `Для постройки нефтяного предприятия необходим 15 уровень.`, { show_alert: true })
|
||
}
|
||
break;
|
||
case 'metall':
|
||
if (user.level < 9) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `Для постройки металлургического предприятия необходим 20 уровень.`, { show_alert: true })
|
||
}
|
||
break;
|
||
case 'gold':
|
||
if (user.level < 12) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `Для постройки золотодобывающего предприятия необходим 25 уровень.`, { show_alert: true })
|
||
}
|
||
break;
|
||
case 'diamond':
|
||
if (user.level < 15) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `Для постройки алмазодобывающего предприятия необходим 30 уровень.`, { show_alert: true })
|
||
}
|
||
break;
|
||
}
|
||
|
||
// Строим предприятие
|
||
let enterpriseName = `${user.username}'s ${getReadableType(type)}` // Название предприятия
|
||
let enterprise = await EnterpriseModel.create({
|
||
name: enterpriseName,
|
||
resourceType: type,
|
||
playerId: user.telegram_id,
|
||
level: 1,
|
||
efficiency: 10,
|
||
warehouseCapacity: 100
|
||
})
|
||
|
||
// Снимаем деньги с баланса
|
||
await user.update({ money: user.money - price })
|
||
|
||
return await ctx.editMessageText(`Предприятие ${enterprise.name} (ID: ${enterprise.id}) построено!`)
|
||
})
|
||
|
||
// Запрашиваем подтверждение на продажу предприятия
|
||
bot.action(/sell_enterprise_(\d+)/, async (ctx) => {
|
||
try {
|
||
const enterpriseId = parseInt(ctx.match[1], 10);
|
||
const enterprise = await EnterpriseModel.findByPk(enterpriseId);
|
||
if (!enterprise) {
|
||
return await ctx.reply('Предприятие не найдено.');
|
||
}
|
||
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (enterprise.playerId !== user.telegram_id) {
|
||
return await ctx.reply('Это не ваше предприятие.');
|
||
}
|
||
// Получаем информацию о наличии грузовика у предприятия и его уровень
|
||
const truck = await TruckModel.findOne({ where: { enterpriseId: enterprise.id } });
|
||
const truckLevel = truck ? truck.level : 0;
|
||
const metallPrice = await ResourcePriceModel.findOne({ where: { resource: 'metall' } });
|
||
const truckPrice = metallPrice.price * 700 * truckLevel;
|
||
// Получаем текущую стоимость предприятия
|
||
const resourcePrice = await ResourcePriceModel.findOne({
|
||
where: { resource: enterprise.resourceType }
|
||
});
|
||
const price = Math.round(resourcePrice.price * 1.5 * 5 * 24 * 7 * enterprise.level + truckPrice * 0.5);
|
||
return await ctx.reply(`Вы уверены, что хотите продать предприятие ${enterprise.name} за ${utils.spaces(price)} руб?`, Markup.inlineKeyboard([
|
||
[{ text: '✅ Да', callback_data: `confirm_enterprise_${enterpriseId}` }],
|
||
[{ text: '❌ Нет', callback_data: 'enterprise_menu' }]
|
||
]).resize());
|
||
} catch (error) {
|
||
console.error('Ошибка при запросе на продажу предприятия:', error);
|
||
return await ctx.reply('Произошла ошибка при запросе на продажу предприятия. Попробуйте позже.');
|
||
}
|
||
});
|
||
|
||
// Продажа предприятия
|
||
bot.action(/confirm_enterprise_(\d+)/, async (ctx) => {
|
||
try {
|
||
const enterpriseId = parseInt(ctx.match[1], 10);
|
||
const enterprise = await EnterpriseModel.findByPk(enterpriseId);
|
||
if (!enterprise) {
|
||
return await ctx.reply('Предприятие не найдено.');
|
||
}
|
||
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (enterprise.playerId !== user.telegram_id) {
|
||
return await ctx.reply('Это не ваше предприятие.');
|
||
}
|
||
// Получаем информацию о наличии грузовика у предприятия и его уровень
|
||
const truck = await TruckModel.findOne({ where: { enterpriseId: enterprise.id } });
|
||
const truckLevel = truck ? truck.level : 1;
|
||
const metallPrice = await ResourcePriceModel.findOne({ where: { resource: 'metall' } });
|
||
const truckPrice = metallPrice.price * 700 * truckLevel;
|
||
// Получаем текущую стоимость предприятия
|
||
const resourcePrice = await ResourcePriceModel.findOne({
|
||
where: { resource: enterprise.resourceType }
|
||
});
|
||
console.log()
|
||
const price = Math.round(resourcePrice.price * 1.5 * 5 * 24 * 7 * enterprise.level + truckPrice * 0.5);
|
||
|
||
// Продаем предприятие
|
||
await enterprise.destroy();
|
||
// Удаляем грузовик
|
||
await TruckModel.destroy({ where: { enterpriseId: enterprise.id } });
|
||
await user.update({ money: user.money + price });
|
||
|
||
return await ctx.reply(`Предприятие ${enterprise.name} продано за ${utils.spaces(price)} руб.`);
|
||
} catch (error) {
|
||
console.error('Ошибка при продаже предприятия:', error);
|
||
return await ctx.reply('Произошла ошибка при продаже предприятия. Попробуйте позже.');
|
||
}
|
||
});
|
||
|
||
// Функция для преобразования типа ресурса в читаемое название
|
||
function getReadableType(type) {
|
||
const names = {
|
||
wood: 'Деревообрабатывающее предприятие',
|
||
coal: 'Угольное предприятие',
|
||
oil: 'Нефтяное предприятие',
|
||
metall: 'Металлургическое предприятие',
|
||
gold: 'Золотое предприятие',
|
||
diamond: 'Алмазное предприятие'
|
||
}
|
||
return names[type] || type
|
||
}
|
||
|
||
function getReadableResourceType(type) {
|
||
const names = {
|
||
wood: 'Дерево',
|
||
coal: 'Уголь',
|
||
oil: 'Нефть',
|
||
metall: 'Металл',
|
||
gold: 'Золото',
|
||
diamond: 'Алмазы'
|
||
}
|
||
return names[type] || type
|
||
}
|
||
|
||
bot.action('buy_warehouse', async (ctx) => {
|
||
try {
|
||
// Получаем пользователя
|
||
let user = await UserModel.findByPk(ctx.from.id);
|
||
if (!user) {
|
||
return await ctx.reply('Произошла ошибка. Пользователь не найден.');
|
||
}
|
||
|
||
if (user.level < 5) {
|
||
return await ctx.reply('Для покупки склада необходим 5 уровень.');
|
||
}
|
||
|
||
// Получаем цены на металл и дерево
|
||
const woodPrice = await ResourcePriceModel.findOne({ where: { resource: 'wood' } });
|
||
const metallPrice = await ResourcePriceModel.findOne({ where: { resource: 'metall' } });
|
||
|
||
if (!woodPrice || !metallPrice) {
|
||
return await ctx.reply('Не удалось получить цены на ресурсы. Попробуйте позже.');
|
||
}
|
||
|
||
// Формируем стоимость склада
|
||
const warehouseBaseCost = 10; // Коэффициент для расчёта
|
||
const warehouseCost = Math.round(
|
||
(woodPrice.price + metallPrice.price) * warehouseBaseCost * 24 * 7
|
||
);
|
||
|
||
// Проверяем, хватает ли денег у пользователя
|
||
if (user.money < warehouseCost) {
|
||
return await ctx.telegram.answerCbQuery(
|
||
ctx.callbackQuery.id,
|
||
`У вас недостаточно средств для покупки склада. Необходимо: ${utils.spaces(warehouseCost)} руб.`,
|
||
{ show_alert: true }
|
||
);
|
||
}
|
||
|
||
// Создаём склад
|
||
await WarehouseModel.create({
|
||
playerId: user.telegram_id,
|
||
capacity: 1000, // Начальная ёмкость
|
||
wood: 0,
|
||
coal: 0,
|
||
oil: 0,
|
||
metall: 0,
|
||
gold: 0,
|
||
diamond: 0,
|
||
});
|
||
|
||
// Списываем деньги
|
||
await user.update({ money: user.money - warehouseCost });
|
||
|
||
// Уведомление об успешной покупке
|
||
return await ctx.reply(
|
||
`Вы успешно купили единый склад за ${warehouseCost} руб!`
|
||
);
|
||
} catch (error) {
|
||
console.error('Ошибка при покупке склада:', error);
|
||
return await ctx.reply('Произошла ошибка при покупке склада. Попробуйте позже.');
|
||
}
|
||
});
|
||
|
||
// Управление складом
|
||
bot.action('manage_warehouse', async (ctx) => {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
const warehouse = await WarehouseModel.findOne({ where: { playerId: user.telegram_id } });
|
||
|
||
if (!warehouse) {
|
||
return await ctx.reply('У вас нет склада для управления.');
|
||
}
|
||
|
||
let message = `🚛 Управление складом:\n`;
|
||
message += `Общая ёмкость: ${warehouse.capacity} ед.\n`;
|
||
message += `Хранимые ресурсы:\n`;
|
||
message += `🌲 Дерево: ${warehouse.wood || 0}\n`;
|
||
message += `⛏️ Уголь: ${warehouse.coal || 0}\n`;
|
||
message += `🛢️ Нефть: ${warehouse.oil || 0}\n`;
|
||
message += `⚙️ Металл: ${warehouse.metall || 0}\n`;
|
||
message += `🥇 Золото: ${warehouse.gold || 0}\n`;
|
||
message += `💎 Алмазы: ${warehouse.diamond || 0}\n`;
|
||
|
||
const buttons = [
|
||
[{ text: '➕ Перевести ресурсы', callback_data: 'transfer_resources' }]
|
||
];
|
||
buttons.push([{ text: '⬅️ Назад', callback_data: 'enterprise_menu' }]);
|
||
return await ctx.editMessageText(message, Markup.inlineKeyboard(buttons).resize());
|
||
});
|
||
|
||
bot.action('transfer_resources', async (ctx) => {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
const warehouse = await WarehouseModel.findOne({ where: { playerId: user.telegram_id } });
|
||
|
||
if (!warehouse) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, 'У вас нет единого склада.');
|
||
}
|
||
|
||
const enterprises = await EnterpriseModel.findAll({
|
||
where: { playerId: user.telegram_id }
|
||
});
|
||
|
||
let message = `➕ Перевозка ресурсов:\n`;
|
||
message += `Общая ёмкость склада: ${warehouse.capacity} ед.\n`;
|
||
message += `Выберите предприятие для перевозки ресурсов:`;
|
||
|
||
const buttons = enterprises.map(ent => {
|
||
return [{ text: `🏭 ${ent.name}`, callback_data: `transfer_from_${ent.id}` }];
|
||
});
|
||
buttons.push([{ text: '⬅️ Назад', callback_data: 'enterprise_menu' }]);
|
||
return await ctx.editMessageText(message, Markup.inlineKeyboard(buttons).resize());
|
||
});
|
||
|
||
// Перевозка с конкретного предприятия
|
||
bot.action(/transfer_from_(\d+)/, async (ctx) => {
|
||
const enterpriseId = ctx.match[1];
|
||
const enterprise = await EnterpriseModel.findByPk(enterpriseId);
|
||
|
||
if (!enterprise) {
|
||
return await ctx.editMessageText('Предприятие не найдено.');
|
||
}
|
||
|
||
const { currentResources, resourceType, playerId } = enterprise;
|
||
|
||
if (currentResources === 0) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, 'На предприятии нет ресурсов для перевода.');
|
||
}
|
||
|
||
const warehouse = await WarehouseModel.findOne({ where: { playerId } });
|
||
|
||
if (!warehouse) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, 'У вас нет склада для перевода ресурсов.');
|
||
}
|
||
|
||
const freeCapacity = warehouse.capacity - getWarehouseUsedCapacity(warehouse);
|
||
const transferAmount = Math.min(currentResources, freeCapacity);
|
||
|
||
if (transferAmount === 0) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, 'Склад заполнен. Перевод невозможен.');
|
||
}
|
||
|
||
// Перевод ресурсов
|
||
warehouse[resourceType] = (warehouse[resourceType] || 0) + transferAmount;
|
||
enterprise.currentResources -= transferAmount;
|
||
|
||
await warehouse.save();
|
||
await enterprise.save();
|
||
|
||
return await ctx.reply(
|
||
`Успешно переведено ${transferAmount} ед. ${resourceType} с предприятия в склад.`
|
||
);
|
||
});
|
||
|
||
bot.command('ent_rename', async (ctx) => {
|
||
let args = ctx.message.text.split(' ').slice(1)
|
||
if (args.length < 2) {
|
||
return await ctx.reply('Использование: /ent_rename <ID> <Новое название>')
|
||
}
|
||
|
||
let enterpriseId = parseInt(args[0])
|
||
let newName = args.slice(1).join(' ')
|
||
|
||
let enterprise = await EnterpriseModel.findByPk(enterpriseId)
|
||
if (!enterprise) {
|
||
return await ctx.reply(`Предприятие с ID ${enterpriseId} не найдено.`)
|
||
}
|
||
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (enterprise.playerId !== user.telegram_id) {
|
||
return await ctx.reply('Это не ваше предприятие.');
|
||
}
|
||
|
||
// Переименовываем предприятие
|
||
await enterprise.update({ name: newName })
|
||
|
||
return await ctx.reply(`Предприятие (ID: ${enterprise.id}) переименовано в "${newName}".`)
|
||
})
|
||
|
||
bot.action(/upgrade_(\d+)/, async (ctx) => {
|
||
let user = await UserModel.findByPk(ctx.from.id)
|
||
let enterpriseId = ctx.match[1]
|
||
let enterprise = await EnterpriseModel.findByPk(enterpriseId)
|
||
|
||
if (!enterprise) return await ctx.reply(`Предприятие не найдено.`)
|
||
const resourcePrice = await ResourcePriceModel.findOne({
|
||
where: { resource: enterprise.resourceType }
|
||
});
|
||
let upgradeCost = resourcePrice.price * 1.5 * 10 * 24 * 7 // Стоимость улучшения зависит от уровня
|
||
if (user.money < upgradeCost) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `У вас недостаточно средств для прокачки предприятия. Необходимо ${upgradeCost} руб.`)
|
||
}
|
||
|
||
if(enterprise.level >= 5) {
|
||
return await ctx.reply('Предприятие уже достигло максимального уровня.')
|
||
}
|
||
|
||
// Прокачка: повышение уровня и эффективности
|
||
await enterprise.update({
|
||
level: enterprise.level + 1,
|
||
efficiency: enterprise.efficiency + 10,
|
||
warehouseCapacity: enterprise.warehouseCapacity + 110
|
||
})
|
||
|
||
// Снимаем деньги с баланса
|
||
await user.update({ money: user.money - upgradeCost })
|
||
|
||
return await ctx.editMessageText(`Предприятие ${enterprise.name} успешно прокачано! Уровень: ${enterprise.level}, Производительность: ${enterprise.efficiency} ед/ч.`)
|
||
})
|
||
|
||
// Показ списка предприятий
|
||
bot.action('sell_resources', async (ctx) => {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
const enterprises = await EnterpriseModel.findAll({
|
||
where: { playerId: user.telegram_id }
|
||
});
|
||
|
||
if (enterprises.length === 0) {
|
||
return await ctx.reply('❌ У вас нет предприятий для продажи ресурсов.');
|
||
}
|
||
|
||
let message = `<b>📜 Список ваших предприятий и их ресурсы:</b>\n\n`;
|
||
enterprises.forEach((enterprise, index) => {
|
||
message += `<b>${index + 1}.</b> ${enterprise.name} (${enterprise.resourceType}): <b>${enterprise.currentResources}</b> единиц\n`;
|
||
});
|
||
|
||
message += `\n<b>📦 Продажа ресурсов:</b>\n`;
|
||
message += `• Для продажи ресурсов с предприятия используйте команду:\n<code>/sellres [номер_предприятия] [количество]</code>\n<i>Пример:</i> <code>/sellres 1 50</code>\n\n`;
|
||
message += `• Для продажи ресурсов со склада используйте команду:\n<code>/sellstorage [тип_ресурса (wood/coal/oil/metall/gold/diamond)] [количество]</code>\n<i>Пример:</i> <code>/sellstorage wood 10</code>`;
|
||
|
||
return await ctx.reply(message, {
|
||
parse_mode: 'HTML',
|
||
reply_markup: {
|
||
inline_keyboard: [
|
||
[{ text: '↩ Назад', callback_data: 'enterprise_menu' }]
|
||
]
|
||
}
|
||
});
|
||
});
|
||
|
||
|
||
// Обработка продажи ресурсов
|
||
bot.command('sellres', async (ctx) => {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
const [enterpriseId, amountToSell] = ctx.message.text.split(' ').slice(1);
|
||
|
||
if (!enterpriseId || !amountToSell) {
|
||
return await ctx.reply('Неверный формат команды. Пример: /sellres 1 50');
|
||
}
|
||
|
||
const enterprise = await EnterpriseModel.findByPk(enterpriseId, {
|
||
where: { playerId: user.telegram_id }
|
||
});
|
||
|
||
if (!enterprise) {
|
||
return await ctx.reply('Предприятие с таким id не найдено.');
|
||
}
|
||
|
||
const amount = parseInt(amountToSell, 10);
|
||
|
||
if (isNaN(amount) || amount <= 0) {
|
||
return await ctx.reply('Укажите корректное количество для продажи.');
|
||
}
|
||
|
||
if (enterprise.currentResources < amount) {
|
||
return await ctx.reply(
|
||
`На предприятии ${enterprise.name} недостаточно ресурсов. Доступно: ${enterprise.currentResources} единиц.`
|
||
);
|
||
}
|
||
|
||
const resourcePrice = await ResourcePriceModel.findOne({
|
||
where: { resource: enterprise.resourceType }
|
||
});
|
||
|
||
if (!resourcePrice) {
|
||
return await ctx.reply('Не удалось получить цену ресурса.');
|
||
}
|
||
|
||
const totalSale = Math.floor(amount * resourcePrice.price);
|
||
|
||
// Обновляем данные
|
||
enterprise.currentResources -= amount;
|
||
await enterprise.save();
|
||
|
||
user.money += totalSale;
|
||
await user.save();
|
||
|
||
// Ослабленная инфляция
|
||
resourcePrice.price = Math.round(
|
||
resourcePrice.price * Math.exp(-resourcePrice.fluctuationRate * (amount / 10)) // Дробное уменьшение инфляции
|
||
);
|
||
|
||
// Гарантируем, что цена не упадет ниже 50% от базовой цены
|
||
resourcePrice.price = Math.max(resourcePrice.price, Math.round(resourcePrice.basePrice * 0.5));
|
||
|
||
await resourcePrice.save();
|
||
updateResourcePricesMessage();
|
||
return await ctx.reply(
|
||
`Вы продали ${amount} единиц ${enterprise.resourceType} с предприятия ${enterprise.name} за ${totalSale} руб.\nТекущая цена за единицу: ${resourcePrice.price.toFixed(0)} руб.`
|
||
);
|
||
});
|
||
|
||
bot.action(/sell_all_(\d+)/, async (ctx) => {
|
||
const enterpriseId = ctx.match[1];
|
||
const enterprise = await EnterpriseModel.findByPk(enterpriseId);
|
||
|
||
if (!enterprise) {
|
||
return await ctx.reply('Предприятие не найдено.');
|
||
}
|
||
|
||
const { currentResources, resourceType, playerId } = enterprise;
|
||
const resourcePrice = await ResourcePriceModel.findOne({ where: { resource: resourceType } });
|
||
const totalPrice = currentResources * resourcePrice.price;
|
||
|
||
if (currentResources === 0) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, 'На предприятии нет ресурсов для продажи.');
|
||
}
|
||
|
||
await ctx.reply(
|
||
`Вы уверены, что хотите продать все ${currentResources} ед. ${resourceType} за ${totalPrice} руб.? Это необратимо!\n\nРекомендуем воспользоваться командой /sell для более гибкого управления продажами.`,
|
||
Markup.inlineKeyboard([
|
||
[
|
||
{ text: '✅ Продать всё', callback_data: `confirm_ressell_${enterpriseId}` },
|
||
{ text: '❌ Отмена', callback_data: 'cancel' }
|
||
]
|
||
])
|
||
);
|
||
});
|
||
|
||
bot.action(/confirm_ressell_(\d+)/, async (ctx) => {
|
||
const enterpriseId = ctx.match[1];
|
||
const enterprise = await EnterpriseModel.findByPk(enterpriseId);
|
||
|
||
if (!enterprise) {
|
||
return await ctx.reply('Предприятие не найдено.');
|
||
}
|
||
|
||
const { currentResources, resourceType, playerId } = enterprise;
|
||
const resourcePrice = await ResourcePriceModel.findOne({ where: { resource: resourceType } });
|
||
const totalPrice = currentResources * resourcePrice.price;
|
||
|
||
if (currentResources === 0) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, 'На предприятии нет ресурсов для продажи.');
|
||
}
|
||
|
||
// Обновление баланса игрока
|
||
const user = await UserModel.findByPk(playerId);
|
||
user.money += totalPrice;
|
||
enterprise.currentResources = 0;
|
||
|
||
await user.save();
|
||
await enterprise.save();
|
||
console.log(resourcePrice.price * Math.exp(-resourcePrice.fluctuationRate * (currentResources / 10)))
|
||
// Ослабленная инфляция
|
||
resourcePrice.price = Math.round(
|
||
resourcePrice.price * Math.exp(-resourcePrice.fluctuationRate * (currentResources / 10)) // Дробное уменьшение инфляции
|
||
);
|
||
// Гарантируем, что цена не упадет ниже 50% от базовой цены
|
||
resourcePrice.price = Math.max(resourcePrice.price, Math.round(resourcePrice.basePrice * 0.5));
|
||
|
||
await resourcePrice.save();
|
||
updateResourcePricesMessage();
|
||
await ctx.editMessageText(`Вы успешно продали ${currentResources} ед. ${getEnterpriseEmoji(resourceType)} за ${totalPrice} руб.`);
|
||
});
|
||
|
||
// Продажа ресурсов со склада
|
||
bot.command('sellstorage', async (ctx) => {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
const [resourceType, amountToSell] = ctx.message.text.split(' ').slice(1);
|
||
|
||
if (!resourceType || !amountToSell) {
|
||
return await ctx.reply('Неверный формат команды. Пример: /sellstorage <тип_ресурса> <количество>');
|
||
}
|
||
|
||
const storage = await WarehouseModel.findOne({
|
||
where: { playerId: user.telegram_id }
|
||
});
|
||
|
||
if (!storage || !storage[resourceType]) {
|
||
return await ctx.reply(`У вас нет ресурсов типа ${resourceType} на складе.`);
|
||
}
|
||
|
||
const amount = parseInt(amountToSell, 10);
|
||
|
||
if (isNaN(amount) || amount <= 0) {
|
||
return await ctx.reply('Укажите корректное количество для продажи.');
|
||
}
|
||
|
||
if (storage[resourceType] < amount) {
|
||
return await ctx.reply(`На складе недостаточно ресурсов типа ${resourceType}. Доступно: ${storage[resourceType]} единиц.`);
|
||
}
|
||
|
||
const resourcePrice = await ResourcePriceModel.findOne({ where: { resource: resourceType } });
|
||
|
||
if (!resourcePrice) {
|
||
return await ctx.reply('Не удалось получить цену ресурса.');
|
||
}
|
||
|
||
const totalSale = Math.floor(amount * resourcePrice.price);
|
||
|
||
// Обновляем данные
|
||
storage[resourceType] -= amount;
|
||
await storage.save();
|
||
|
||
user.money += totalSale;
|
||
await user.save();
|
||
|
||
// Ослабленная инфляция
|
||
resourcePrice.price = Math.round(
|
||
resourcePrice.price * Math.exp(-resourcePrice.fluctuationRate * (amount / 10)) // Дробное уменьшение инфляции
|
||
);
|
||
|
||
// Гарантируем, что цена не упадет ниже 50% от базовой цены
|
||
resourcePrice.price = Math.max(resourcePrice.price, Math.round(resourcePrice.basePrice * 0.5));
|
||
|
||
await resourcePrice.save();
|
||
updateResourcePricesMessage();
|
||
return await ctx.reply(
|
||
`Вы продали ${amount} ед. ${resourceType} со склада за ${totalSale} руб.\nТекущая цена за единицу: ${resourcePrice.price.toFixed(0)} руб.`
|
||
);
|
||
});
|
||
|
||
// Обработка найма грузовика для выбранного предприятия
|
||
bot.action(/hire_truck_(\d+)/, async (ctx) => {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
const enterpriseId = ctx.match[1];
|
||
|
||
// Находим предприятие
|
||
const enterprise = await EnterpriseModel.findByPk(enterpriseId);
|
||
|
||
if (!enterprise || enterprise.playerId != user.telegram_id) {
|
||
return await ctx.reply('Предприятие не найдено или вам не принадлежит.');
|
||
}
|
||
|
||
// Проверяем, есть ли уже грузовик для этого предприятия
|
||
const existingTruck = await TruckModel.findOne({
|
||
where: { enterpriseId: enterprise.id }
|
||
});
|
||
|
||
if (existingTruck) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `У вас уже есть грузовик для предприятия ${enterprise.name}.`);
|
||
}
|
||
const resourcePrice = await ResourcePriceModel.findOne({ where: { resource: 'metall' } });
|
||
if (!resourcePrice) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, 'Не удалось получить цену ресурса.');
|
||
}
|
||
if (user.money < resourcePrice.price * 700) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `Недостаточно средств. Стоимость грузовика: ${utils.spaces(resourcePrice.price * 700)} руб.`);
|
||
}
|
||
await user.update({ money: user.money - (resourcePrice.price * 700) })
|
||
// Создаем новый грузовик
|
||
const newTruck = await TruckModel.create({
|
||
enterpriseId: enterprise.id,
|
||
capacity: 10, // Начальная вместимость
|
||
efficiency: 1 // Начальная эффективность
|
||
});
|
||
return await ctx.reply(
|
||
`Вы успешно наняли грузовик для предприятия ${enterprise.name}!`
|
||
);
|
||
});
|
||
|
||
|
||
async function initializePrices() {
|
||
const resources = [
|
||
{ resource: 'wood', price: 1000, basePrice: 1000, fluctuationRate: 0.002, recoveryRate: 0.15 },
|
||
{ resource: 'coal', price: 2000, basePrice: 2000, fluctuationRate: 0.002, recoveryRate: 0.12 },
|
||
{ resource: 'oil', price: 5000, basePrice: 5000, fluctuationRate: 0.001, recoveryRate: 0.1 },
|
||
{ resource: 'metall', price: 5000, basePrice: 5000, fluctuationRate: 0.001, recoveryRate: 0.1 },
|
||
{ resource: 'gold', price: 10000, basePrice: 10000, fluctuationRate: 0.0005, recoveryRate: 0.08 },
|
||
{ resource: 'diamond', price: 25000, basePrice: 25000, fluctuationRate: 0.0004, recoveryRate: 0.05 },
|
||
];
|
||
|
||
for (const resource of resources) {
|
||
await ResourcePriceModel.findOrCreate({
|
||
where: { resource: resource.resource },
|
||
defaults: resource,
|
||
});
|
||
}
|
||
}
|
||
|
||
initializePrices();
|
||
|
||
const recoverResourcePrices = async () => {
|
||
const resources = await ResourcePriceModel.findAll();
|
||
|
||
for (const resource of resources) {
|
||
// Определяем случайное колебание в диапазоне [-maxFluctuation, maxFluctuation]
|
||
const maxFluctuation = resource.basePrice * 0.3; // 30% от базовой цены
|
||
const priceFluctuation = (Math.random() * 2 - 1) * maxFluctuation;
|
||
|
||
// Применяем колебание
|
||
resource.price = Math.round(resource.price + priceFluctuation);
|
||
|
||
// Если цена выше базовой, шанс на её снижение
|
||
if (resource.price > resource.basePrice) {
|
||
const chanceToDrop = 0.5; // 50% шанс на снижение
|
||
if (Math.random() < chanceToDrop) {
|
||
resource.price -= Math.round(resource.basePrice * 0.1); // Снижение на 1% базовой цены
|
||
}
|
||
}
|
||
|
||
// Если цена ниже базовой, восстанавливаем с учётом recoveryRate
|
||
if (resource.price < resource.basePrice) {
|
||
resource.price = Math.min(
|
||
Math.round(resource.price + resource.basePrice * resource.recoveryRate),
|
||
resource.basePrice // Не превышать базовую цену
|
||
);
|
||
}
|
||
|
||
if (resource.price > resource.basePrice * 2.1) {
|
||
resource.price = Math.round(resource.basePrice * 0.79764918); // Устанавливаем цену на уровне 1.5 базовой
|
||
}
|
||
|
||
// Гарантируем, что цена остаётся в диапазоне [50% basePrice, 150% basePrice]
|
||
resource.price = Math.max(resource.price, Math.round(resource.basePrice * 0.5)); // Минимум
|
||
resource.price = Math.min(resource.price, Math.round(resource.basePrice * 3)); // Максимум
|
||
|
||
await resource.save(); // Сохраняем после всех корректировок
|
||
}
|
||
};
|
||
|
||
// Функция добычи ресурсов
|
||
const resourceProduction = async () => {
|
||
try {
|
||
// Получаем все предприятия
|
||
const enterprises = await EnterpriseModel.findAll();
|
||
|
||
for (const enterprise of enterprises) {
|
||
const {
|
||
id,
|
||
efficiency,
|
||
warehouseCapacity,
|
||
resourceType,
|
||
currentResources,
|
||
level,
|
||
playerId
|
||
} = enterprise;
|
||
|
||
// Расчёт добытых ресурсов
|
||
const producedResources = efficiency;
|
||
|
||
// Проверяем ёмкость склада предприятия
|
||
if (currentResources + producedResources <= warehouseCapacity) {
|
||
enterprise.currentResources += producedResources;
|
||
await enterprise.save();
|
||
console.log(
|
||
`Предприятие ${id}: добыто ${producedResources} единиц ${resourceType}.`
|
||
);
|
||
} else {
|
||
console.log(`Предприятие ${id}: склад заполнен.`);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Ошибка добычи ресурсов:', error);
|
||
}
|
||
};
|
||
|
||
// Функция транспортировки ресурсов
|
||
const resourceTransportation = async () => {
|
||
try {
|
||
// Получаем все предприятия
|
||
const enterprises = await EnterpriseModel.findAll();
|
||
|
||
for (const enterprise of enterprises) {
|
||
const {
|
||
id,
|
||
name,
|
||
resourceType,
|
||
currentResources,
|
||
playerId
|
||
} = enterprise;
|
||
|
||
if (currentResources > 0) {
|
||
// Получаем грузовики, привязанные к предприятию
|
||
const trucks = await TruckModel.findAll({
|
||
where: { enterpriseId: id }
|
||
});
|
||
|
||
if (trucks.length === 0) {
|
||
console.log(
|
||
`[${id}] ${name}: нет доступных грузовиков для транспортировки.`
|
||
);
|
||
continue;
|
||
}
|
||
|
||
// Получаем универсальный склад игрока
|
||
const warehouse = await WarehouseModel.findOne({
|
||
where: { playerId }
|
||
});
|
||
|
||
if (!warehouse) {
|
||
console.log(
|
||
`[${id}] ${name}: у игрока ${playerId} нет универсального склада.`
|
||
);
|
||
continue;
|
||
}
|
||
|
||
// Транспортировка ресурсов
|
||
let totalTransported = 0;
|
||
for (const truck of trucks) {
|
||
const transportableAmount = Math.min(
|
||
truck.capacity, // Максимальная вместимость грузовика
|
||
currentResources, // Доступные ресурсы на предприятии
|
||
warehouse.capacity - getWarehouseUsedCapacity(warehouse) // Свободное место на складе
|
||
);
|
||
|
||
if (transportableAmount > 0) {
|
||
// Обновляем данные склада
|
||
warehouse[resourceType] =
|
||
(warehouse[resourceType] || 0) + transportableAmount;
|
||
|
||
// Уменьшаем ресурсы на предприятии
|
||
enterprise.currentResources -= transportableAmount;
|
||
|
||
await warehouse.save();
|
||
await enterprise.save();
|
||
|
||
totalTransported += transportableAmount;
|
||
|
||
console.log(
|
||
`Грузовик ${truck.id} перевёз ${transportableAmount} единиц ${resourceType}.`
|
||
);
|
||
}
|
||
|
||
// Прекращаем, если ресурсы закончились
|
||
if (enterprise.currentResources === 0) break;
|
||
}
|
||
|
||
if (totalTransported === 0) {
|
||
console.log(
|
||
`[${id}] ${name}: склад заполнен, транспортировка невозможна.`
|
||
);
|
||
}
|
||
} else {
|
||
console.log(`[${id}] ${name}: нет ресурсов для транспортировки.`);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Ошибка транспортировки ресурсов:', error);
|
||
}
|
||
};
|
||
|
||
// Расчёт используемой ёмкости склада
|
||
const getWarehouseUsedCapacity = (warehouse) => {
|
||
const resources = ['wood', 'coal', 'oil', 'metall', 'gold', 'diamond'];
|
||
return resources.reduce((sum, resource) => sum + (warehouse[resource] || 0), 0);
|
||
};
|
||
|
||
const CHANNEL_ID = '@CampFireGameBotNews'; // Замените на username вашего канала
|
||
let resourceMessageId = 18; // Хранение ID сообщения
|
||
|
||
const previousPrices = {}; // Объект для хранения предыдущих цен
|
||
|
||
const getResourcePricesText = async () => {
|
||
const resources = await ResourcePriceModel.findAll(); // Получаем данные из базы
|
||
let message = '💰 **Актуальные цены на ресурсы:**\n\n';
|
||
|
||
resources.forEach((resource) => {
|
||
const currentPrice = resource.price;
|
||
const previousPrice = previousPrices[resource.resource] || currentPrice; // Берем предыдущую цену или текущую, если нет данных
|
||
|
||
// Определяем эмодзи изменения цены
|
||
let trendEmoji = '⚖️'; // По умолчанию стабильность
|
||
if (currentPrice > previousPrice) {
|
||
trendEmoji = '📈'; // Рост
|
||
} else if (currentPrice < previousPrice) {
|
||
trendEmoji = '📉'; // Падение
|
||
}
|
||
|
||
// Сохраняем текущую цену как предыдущую для следующего сравнения
|
||
previousPrices[resource.resource] = currentPrice;
|
||
|
||
// Добавляем строку с ценой и эмодзи
|
||
message += `• ${getEnterpriseEmoji(resource.resource)} ${getReadableResourceType(resource.resource)}: ${trendEmoji} ${utils.spaces(currentPrice)} руб/ед.\n`;
|
||
});
|
||
|
||
message += `\n🔁 Последнее обновление цены: ${utils.getCurrentTime()}`;
|
||
return message;
|
||
};
|
||
|
||
// Функция для отправки/обновления сообщения
|
||
const updateResourcePricesMessage = async () => {
|
||
const pricesText = await getResourcePricesText();
|
||
|
||
try {
|
||
if (resourceMessageId) {
|
||
// Если сообщение существует, обновляем его
|
||
await bot.telegram.editMessageText(CHANNEL_ID, resourceMessageId, null, pricesText, {
|
||
parse_mode: 'Markdown',
|
||
});
|
||
} else {
|
||
// Если сообщения нет, отправляем новое
|
||
const sentMessage = await bot.telegram.sendMessage(CHANNEL_ID, pricesText, {
|
||
parse_mode: 'Markdown',
|
||
});
|
||
resourceMessageId = sentMessage.message_id; // Сохраняем ID нового сообщения
|
||
console.log("ID сообщения с котировками: " + sentMessage.message_id)
|
||
}
|
||
} catch (error) {
|
||
console.error('Ошибка при обновлении сообщения с ценами ресурсов:', error);
|
||
}
|
||
};
|
||
|
||
const resetCasinoFlag = async () => {
|
||
try {
|
||
await UserModel.update(
|
||
{ isPlayingCasino: false }, // Новое значение для поля
|
||
{ where: { isPlayingCasino: true } } // Только для тех, у кого флаг был установлен
|
||
);
|
||
console.log('Флаг isPlayingCasino успешно сброшен для всех пользователей.');
|
||
} catch (error) {
|
||
console.error('Ошибка при сбросе флага isPlayingCasino:', error);
|
||
}
|
||
};
|
||
|
||
// Вызов функции при запуске бота
|
||
resetCasinoFlag();
|
||
|
||
// Запускаем функцию
|
||
updateResourcePricesMessage();
|
||
// Запускаем обновление каждые 15 минут
|
||
schedule.scheduleJob('*/15 * * * *', recoverResourcePrices);
|
||
schedule.scheduleJob('*/15 * * * *', updateResourcePricesMessage);
|
||
// Запускаем процессы каждый час
|
||
schedule.scheduleJob('0 * * * *', resourceProduction); // Каждый час в начале часа
|
||
schedule.scheduleJob('5 * * * *', resourceTransportation); // Каждый час
|
||
|
||
|
||
bot.command('force_prod', async (ctx) => {
|
||
resourceProduction()
|
||
return await ctx.reply(`Принудительно добыто.`)
|
||
})
|
||
|
||
bot.command('force_transport', async (ctx) => {
|
||
resourceTransportation()
|
||
return await ctx.reply(`Принудительно перевезено.`)
|
||
})
|
||
|
||
bot.command('force_hour', async (ctx) => {
|
||
resourceProduction()
|
||
resourceTransportation()
|
||
return await ctx.reply(`Принудительный час.`)
|
||
})
|
||
|
||
bot.command('force_recover', async (ctx) => {
|
||
recoverResourcePrices()
|
||
updateResourcePricesMessage()
|
||
return await ctx.reply(`Принудительно.`)
|
||
})
|
||
bot.command('force_mat', async (ctx) => {
|
||
utils.matPriceUpdate()
|
||
return await ctx.reply(`Принудительный материал.`)
|
||
})
|
||
/////////////////////////////////////Enterprise Update end//////////////////////////////////////////////////
|
||
|
||
/////////////////////////////////////Casino Update//////////////////////////////////////////////////
|
||
// Функция для игры в слоты
|
||
const spinSlots = async (ctx, bet, user) => {
|
||
const slotIcons = ['🔥', '⚡', '⭐', '💀']; // Иконки для слотов
|
||
|
||
// Определяем результат игры
|
||
const winMultiplier = determineWin();
|
||
const finalIcons = generateVisualResult(winMultiplier, slotIcons);
|
||
|
||
// Вычисляем выигрыш/проигрыш
|
||
let resultMessage = `🎰 Итог:\n${finalIcons.join(' | ')}\n\n`;
|
||
if (winMultiplier > 0) {
|
||
const winnings = bet * winMultiplier;
|
||
utils.giveExp(user, winMultiplier * 10)
|
||
user.money += winnings;
|
||
await user.save();
|
||
console.log(`Win: ${winnings}`)
|
||
resultMessage += `🎉 Вы выиграли ₽${spaces(winnings)}! Множитель: x${winMultiplier}`;
|
||
} else {
|
||
utils.giveExp(user, 2)
|
||
user.money -= bet;
|
||
await user.save();
|
||
resultMessage += `💔 Вы проиграли ₽${spaces(bet)}. Попробуйте ещё раз!`;
|
||
}
|
||
|
||
// Отправляем результат
|
||
await ctx.answerCbQuery(resultMessage, { show_alert: true });
|
||
};
|
||
|
||
// Функция для определения выигрыша
|
||
const determineWin = () => {
|
||
const probabilities = {
|
||
25: 0.001, // 0.01% шанс на x25
|
||
10: 0.009, // 0.9% шанс на x10
|
||
5: 0.01, // 1% шанс на x5
|
||
2: 0.09, // 9% шанс на x2
|
||
0: 0.9, // 90% шанс на проигрыш
|
||
};
|
||
|
||
const random = Math.random();
|
||
let cumulativeProbability = 0;
|
||
|
||
for (const [multiplier, probability] of Object.entries(probabilities)) {
|
||
cumulativeProbability += probability;
|
||
if (random < cumulativeProbability) {
|
||
return parseInt(multiplier, 10);
|
||
}
|
||
}
|
||
return 0; // По умолчанию проигрыш
|
||
};
|
||
|
||
// Генерация визуального результата на основе выигрыша
|
||
const generateVisualResult = (multiplier, slotIcons) => {
|
||
if (multiplier === 25) return ['🔥', '🔥', '🔥'];
|
||
if (multiplier === 10) return ['⚡', '⚡', '⚡'];
|
||
if (multiplier === 5) return ['⭐', '⭐', '⭐'];
|
||
if (multiplier === 2) return ['💀', '💀', '💀'];
|
||
|
||
// Генерация случайных иконок для проигрыша
|
||
let icons = [
|
||
slotIcons[Math.floor(Math.random() * slotIcons.length)],
|
||
slotIcons[Math.floor(Math.random() * slotIcons.length)],
|
||
slotIcons[Math.floor(Math.random() * slotIcons.length)],
|
||
];
|
||
|
||
// Проверка на уникальность
|
||
if (icons[0] === icons[1] && icons[1] === icons[2]) {
|
||
let newIcon;
|
||
do {
|
||
newIcon = slotIcons[Math.floor(Math.random() * slotIcons.length)];
|
||
} while (newIcon === icons[0]);
|
||
|
||
// Заменяем одну из иконок (например, третью)
|
||
icons[2] = newIcon;
|
||
}
|
||
|
||
return icons;
|
||
};
|
||
|
||
|
||
// Основная команда слотов
|
||
bot.action(/slots\d+/, async (ctx) => {
|
||
try {
|
||
const data = ctx.update.callback_query.data;
|
||
const bet = parseInt(data.replace('slots', ''), 10);
|
||
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
|
||
if (!user || user.money < bet) {
|
||
return ctx.answerCbQuery('💸 У вас недостаточно средств для ставки.', { show_alert: true });
|
||
}
|
||
if (user.isPlayingCasino) {
|
||
return ctx.answerCbQuery('📛 Спам атака предотвращена.', { show_alert: true });
|
||
}
|
||
const timer = user.slottime;
|
||
const cooldown = utils.setCooldown(user, 10, timer);
|
||
if (user.slottime > cooldown.currentTime) {
|
||
return ctx.answerCbQuery('📛 Слоты будут доступны через пару секунд.', { show_alert: true });
|
||
}
|
||
|
||
// Установить флаг игры
|
||
user.isPlayingCasino = true;
|
||
user.slottime = cooldown.endTime;
|
||
await user.save();
|
||
|
||
// Запуск игры
|
||
await spinSlots(ctx, bet, user);
|
||
|
||
// Сброс флага игры
|
||
user.isPlayingCasino = false;
|
||
await user.save();
|
||
} catch (error) {
|
||
console.error('Ошибка при игре в слоты:', error);
|
||
return ctx.reply('Произошла ошибка. Попробуйте снова.');
|
||
}
|
||
});
|
||
|
||
// Функция для форматирования чисел
|
||
const spaces = (number) => {
|
||
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
|
||
};
|
||
|
||
bot.hears('🎰 Рулетка', async (ctx) => {
|
||
return ctx.reply('Закрыто')
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
if (user.money <= 0) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `У вас недостаточно средств для игры.`, { show_alert: true });
|
||
}
|
||
|
||
// Инициализация ставки через ctx.session
|
||
if (!ctx.session.betAmount) {
|
||
ctx.session.betAmount = 10000; // Начальная ставка
|
||
}
|
||
|
||
// Вспомогательная функция для генерации случайного числа рулетки
|
||
const spinRoulette = () => {
|
||
// Все нечетные числа (красные)
|
||
const redNumbers = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35];
|
||
// Все четные числа (черные)
|
||
const blackNumbers = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36];
|
||
|
||
// Рулетка: делаем случайный выбор
|
||
const randomIndex = Math.floor(Math.random() * 37); // от 0 до 36
|
||
return randomIndex;
|
||
};
|
||
|
||
// Кнопки для выбора ставки
|
||
const buttons = [
|
||
[{ text: 'Красный', callback_data: 'red' }],
|
||
[{ text: 'Черный', callback_data: 'black' }],
|
||
[{ text: 'Выбрать номер', callback_data: 'choose_number' }],
|
||
[{ text: `Ставка: ₽${ctx.session.betAmount}`, callback_data: 'no_action' }],
|
||
[
|
||
{ text: '⬆️ Повысить ставку', callback_data: 'increase_bet' },
|
||
{ text: '⬇️ Понизить ставку', callback_data: 'decrease_bet' }
|
||
]
|
||
];
|
||
|
||
const message = `🎰 Добро пожаловать в рулетку!\nВыберите ставку: Красный, Черный или Номер (0-36).`;
|
||
|
||
await ctx.reply(message, Markup.inlineKeyboard(buttons));
|
||
|
||
// Обработчик для изменения ставки
|
||
bot.action('increase_bet', async (ctx) => {
|
||
if (ctx.session.betAmount < 50000) {
|
||
ctx.session.betAmount += 10000; // Увеличиваем ставку на 10000
|
||
const buttons = [
|
||
[{ text: 'Красный', callback_data: 'red' }],
|
||
[{ text: 'Черный', callback_data: 'black' }],
|
||
[{ text: 'Выбрать номер', callback_data: 'choose_number' }],
|
||
[{ text: `Ставка: ₽${ctx.session.betAmount}`, callback_data: 'no_action' }],
|
||
[
|
||
{ text: '⬆️ Повысить ставку', callback_data: 'increase_bet' },
|
||
{ text: '⬇️ Понизить ставку', callback_data: 'decrease_bet' }
|
||
]
|
||
];
|
||
await ctx.editMessageText(`🎰 Добро пожаловать в рулетку!\nВыберите ставку: Красный, Черный или Номер (0-36).`, Markup.inlineKeyboard(buttons));
|
||
} else {
|
||
await ctx.reply('Максимальная ставка - ₽50000');
|
||
}
|
||
});
|
||
|
||
bot.action('decrease_bet', async (ctx) => {
|
||
if (ctx.session.betAmount > 10000) {
|
||
ctx.session.betAmount -= 10000; // Уменьшаем ставку на 10000
|
||
const buttons = [
|
||
[{ text: 'Красный', callback_data: 'red' }],
|
||
[{ text: 'Черный', callback_data: 'black' }],
|
||
[{ text: 'Выбрать номер', callback_data: 'choose_number' }],
|
||
[{ text: `Ставка: ₽${ctx.session.betAmount}`, callback_data: 'no_action' }],
|
||
[
|
||
{ text: '⬆️ Повысить ставку', callback_data: 'increase_bet' },
|
||
{ text: '⬇️ Понизить ставку', callback_data: 'decrease_bet' }
|
||
]
|
||
];
|
||
await ctx.editMessageText(`🎰 Добро пожаловать в рулетку!\nВыберите ставку: Красный, Черный или Номер (0-36).`, Markup.inlineKeyboard(buttons));
|
||
} else {
|
||
await ctx.reply('Минимальная ставка - ₽10000');
|
||
}
|
||
});
|
||
|
||
bot.action(['red', 'black', 'choose_number'], async (ctx) => {
|
||
const data = ctx.update.callback_query.data;
|
||
if (user.money < ctx.session.betAmount) {
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, `У вас недостаточно средств для этой ставки.`, { show_alert: true });
|
||
}
|
||
|
||
let winMultiplier = 0;
|
||
let betResult = '';
|
||
const spinResult = spinRoulette();
|
||
|
||
if (data === 'red') {
|
||
if ([1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35].includes(spinResult)) {
|
||
winMultiplier = 2;
|
||
betResult = `Вы выиграли! 🎉 Красный! Множитель: 2x`;
|
||
} else {
|
||
betResult = `Вы проиграли. Черный номер. 😢`;
|
||
}
|
||
} else if (data === 'black') {
|
||
if ([0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36].includes(spinResult)) {
|
||
winMultiplier = 2;
|
||
betResult = `Вы выиграли! 🎉 Черный! Множитель: 2x`;
|
||
} else {
|
||
betResult = `Вы проиграли. Красный номер. 😢`;
|
||
}
|
||
} else if (data === 'choose_number') {
|
||
const number = Math.floor(Math.random() * 37); // Игрок выбирает случайное число
|
||
if (spinResult === number) {
|
||
winMultiplier = 35; // Огромный выигрыш для точного угадывания
|
||
betResult = `Вы угадали номер! 🎉 Номер ${number}! Множитель: 35x`;
|
||
} else {
|
||
betResult = `Вы не угадали номер. Выпал номер ${spinResult}. 😢`;
|
||
}
|
||
}
|
||
|
||
// Считаем выигрыш
|
||
let prize = ctx.session.betAmount * winMultiplier;
|
||
user.money += prize; // Вычитаем ставку, если выиграли
|
||
await user.save();
|
||
|
||
// Отправляем результат
|
||
return await ctx.telegram.answerCbQuery(ctx.callbackQuery.id, betResult, { show_alert: true });
|
||
});
|
||
});
|
||
|
||
|
||
/////////////////////////////////////Casino Update end//////////////////////////////////////////////////////
|
||
|
||
/////////////////////////////////////Admin Commands//////////////////////////////////////////////////
|
||
|
||
|
||
bot.command('answer', commands.reportAnswer)
|
||
|
||
bot.command('fastblock', commands.fastblock)
|
||
|
||
bot.command('createpromocode', commands.createPromo)
|
||
|
||
bot.command('genpromo', commands.genPromo)
|
||
|
||
bot.command('ban', async (ctx) => {
|
||
const user = await UserModel.findByPk(ctx.from.id);
|
||
|
||
// Проверка прав администратора
|
||
if (user.status !== 'admin') {
|
||
return await ctx.reply(`❌ У вас нет прав для выполнения этой команды.`);
|
||
}
|
||
|
||
// Разбор аргументов команды
|
||
const args = ctx.message.text.split(' ').slice(1);
|
||
if (args.length < 3) {
|
||
return await ctx.reply(`⚠️ Использование: /ban <ID|username> <время в минутах> <причина>.`);
|
||
}
|
||
|
||
const identifier = args[0]; // ID или username пользователя
|
||
const blockTimeMinutes = parseInt(args[1], 10); // Время блокировки в минутах
|
||
const reason = args.slice(2).join(' '); // Причина блокировки
|
||
|
||
// Проверка времени блокировки
|
||
if (isNaN(blockTimeMinutes) || blockTimeMinutes <= 0) {
|
||
return await ctx.reply(`❌ Укажите корректное время блокировки в минутах.`);
|
||
}
|
||
|
||
// Ищем пользователя по Telegram ID или никнейму
|
||
let targetUser;
|
||
if (identifier.startsWith('@')) {
|
||
// Если передан username
|
||
targetUser = await UserModel.findOne({ where: { username: identifier.slice(1) } });
|
||
} else {
|
||
// Если передан Telegram ID
|
||
targetUser = await UserModel.findOne({ where: { telegram_id: identifier } });
|
||
}
|
||
|
||
if (!targetUser) {
|
||
return await ctx.reply(`❌ Пользователь с идентификатором "${identifier}" не найден.`);
|
||
}
|
||
|
||
// Создаём запись о блокировке
|
||
const block = await BlockModel.create({
|
||
telegram_id: targetUser.telegram_id,
|
||
isBlocked: true,
|
||
reason: reason,
|
||
time: Math.trunc(Date.now() / 1000 + blockTimeMinutes * 60) // Время блокировки в секундах
|
||
});
|
||
|
||
// Уведомляем заблокированного пользователя
|
||
try {
|
||
await bot.telegram.sendMessage(
|
||
targetUser.telegram_id,
|
||
`❌ Вы были заблокированы администратором ${user.username}.\nПричина: ${reason}\nСрок: ${blockTimeMinutes} минут(ы).`
|
||
);
|
||
} catch (error) {
|
||
console.error(`Ошибка отправки уведомления пользователю ${targetUser.telegram_id}:`, error);
|
||
}
|
||
|
||
// Уведомляем администратора
|
||
return await ctx.reply(
|
||
`✅ Пользователь ${targetUser.username || targetUser.telegram_id} был успешно заблокирован.\nПричина: ${reason}\nСрок: ${blockTimeMinutes} минут(ы).`
|
||
);
|
||
});
|
||
|
||
///////////////////////////////////////Functions//////////////////////////////////////////////////////
|
||
|
||
setInterval(() => {
|
||
var today = new Date();
|
||
let hours = today.getHours();
|
||
if (hours == "0" || hours == "12") {
|
||
//weaponShopUpdate()
|
||
utils.matPriceUpdate()
|
||
}
|
||
/*if (hours == "9" || hours == "18" || hours == "12") {
|
||
generatePromo()
|
||
}*/
|
||
}, 3600000);
|
||
|
||
// cron на проверку остается ли у пользователя привелегия user.status == 'bronze' || user.status == 'silver' || user.status == 'gold' из user.statustime (оставшиеся минуты)
|
||
schedule.scheduleJob('*/1 * * * *', async () => {
|
||
const users = await UserModel.findAll();
|
||
for (const user of users) {
|
||
if (user.status == 'bronze' || user.status == 'silver' || user.status == 'gold') {
|
||
if (user.statustime > 0) {
|
||
user.statustime -= 1
|
||
await user.save()
|
||
} else {
|
||
user.status = 'user'
|
||
await user.save()
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
start()
|
||
|
||
bot.launch()
|