Test public pve
This commit is contained in:
Degradin 2025-01-14 20:09:13 +03:00
parent 7813a7cde0
commit d0dbf635ec
6 changed files with 1256 additions and 86 deletions

12
bot.js
View File

@ -23,7 +23,8 @@ const {
SaleModel,
ResourcePriceModel,
SkillsModel,
DailyModel
DailyModel,
logMiddleware
} = global.config
const bot = new Telegraf(process.env.BOT_TOKEN)
const rpg = require('./rpg'); // Подключение RPG-механик
@ -76,7 +77,6 @@ bot.telegram.setMyCommands([{
description: "Создать жалобу/обращение/идею."
}
])
bot.use(stage.middleware())
bot.use(rpg)
@ -211,11 +211,19 @@ bot.command('start', async (ctx) => {
['🎁 Бонус'],
['📦 Контейнеры'],
['📢 Вакансии', '🔵 Имущество', '📞 Пригласить'],
['🛡️ Test'],
])
.resize()
)
})
bot.hears('🛡️ Test', async (ctx) => {
const buttons = [
[Markup.button.callback('PVE', 'locations')],
];
ctx.reply(`🚨 Добро пожаловать в тестовый режим 🚨\nВесь функционал в этом режиме еще не сбалансирован\n`, Markup.inlineKeyboard(buttons));
})
bot.hears('Криминал', async (ctx) => {
await ctx.scene.enter('Crime')
})

View File

@ -57,4 +57,6 @@ module.exports = {
343500, 349550, 355650, 361800, 368000
],
topSym : ['🥇', '🥈', '🥉', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '🔟', '🌫️', '🌫️', '🌫️', '🌫️', '🌫️'],
logs: require('../utils/logs'),
logMiddleware: require('../utils/logMiddleware'),
}

1122
json/logs.json Normal file

File diff suppressed because it is too large Load Diff

120
rpg.js
View File

@ -28,7 +28,8 @@ const {
TruckModel,
ResourcePriceModel,
SkillsModel,
DailyModel
DailyModel,
logs
} = global.config
const rpg = new Composer();
@ -37,7 +38,6 @@ rpg.use(async (ctx, next) => {
let id = ctx.from.id
let username = ctx.from.username;
if (username == null) username = ctx.from.id;
const currentTime = utils.getCurrentTime();
switch (ctx.updateType) {
@ -482,7 +482,6 @@ rpg.action(/brutecard_/, async (ctx) => {
if (!emulator) {
return ctx.reply('Для запуска брутфорса вам нужен "Эмулятор картридера".');
}
// Удаляем "Эмулятор картридера" из инвентаря
await InventoryModel.destroy({ where: { id: emulator.id } });
@ -519,7 +518,7 @@ rpg.action(/brutecard_/, async (ctx) => {
const keyboard = Markup.inlineKeyboard(
rows.sort(() => Math.random() - 0.5).map((row) => row) // Сортируем строки кнопок случайным образом
);
logs(ctx, "Брутфорс карты", {card: card, keyboard: keyboard, emulator: emulator});
// Отправляем сообщение с клавиатурой
await ctx.reply(
`💳 Брутфорс карты\n
@ -587,6 +586,7 @@ rpg.action(/brute_fail_/, async (ctx) => {
const keyboard = Markup.inlineKeyboard(
rows.sort(() => Math.random() - 0.5).map((row) => row)
);
logs(ctx, "Брутфорс карты (Fail)", {card: card, keyboard: keyboard});
// Обновляем сообщение с клавиатурой
await ctx.editMessageText(
@ -616,7 +616,7 @@ rpg.action(/brute_success_/, async (ctx) => {
delete attempts[ctx.from.id];
const amount = card.balance; // Сумма денег за карточку
const character = await CharacterModel.findByPk(ctx.from.id);
logs(ctx, "Брутфорс карты (Success)", {card: card, amount: amount});
// Увеличиваем грязные деньги пользователя
character.dirtymoney += amount;
await character.save();
@ -670,6 +670,7 @@ rpg.action(`POCKET_ACTION`, async (ctx) => {
if (character.pocketstealcd > cooldown.currentTime) return ctx.editMessageText(`📛 Данное действие будет доступно через ${cooldown.timeLeftInMinutes} мин.`);
character.pocketstealcd = cooldown.endTime
character.stamina -= 10
logs(ctx, "Карманная кража (Start)", { cdBefore: pocketsteal, cdAfter: character.pocketstealcd, stamina: character.stamina });
character.save()
ctx.editMessageText('Выберите объект', Markup.inlineKeyboard([
[
@ -710,6 +711,7 @@ rpg.action(`MONEY_IN_POCKET`, async (ctx) => {
let moneyIn = utils.rand(5, 1000);
Exp(ctx, character, character.intelligence + 3)
character.dirtymoney += moneyIn;
logs(ctx, "Карманная кража (Кошелек)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, moneyIn: moneyIn});
await character.save();
return ctx.editMessageText(`Вы успешно украли Ð${utils.spaces(moneyIn)} из кармана.`);
});
@ -735,6 +737,7 @@ rpg.action(`PHONE`, async (ctx) => {
return await ctx.reply(`Вы сбыли украденный ${phones[randPhone].name} за Ð${utils.spaces(dirtyMoney)}`)
}
property.mobile = phones[randPhone]
logs(ctx, "Карманная кража (Телефон)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, phone: phones[randPhone]});
await character.save()
await property.save()
return ctx.editMessageText(`Вы успешно украли ${phones[randPhone].name} из кармана.`)
@ -763,6 +766,7 @@ rpg.action(`MONEY_IN_WALLET`, async (ctx) => {
let moneyIn = utils.rand(1000, 10000)
Exp(ctx, character, character.intelligence + 5)
character.dirtymoney += moneyIn
logs(ctx, "Карманная кража (Бумажник)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, moneyIn: moneyIn});
character.save()
return ctx.editMessageText(`Вы успешно украли Ð${utils.spaces(moneyIn)} из бумажника.`)
});
@ -778,7 +782,8 @@ rpg.action(`CARD_IN_WALLET`, async (ctx) => {
return ctx.editMessageText('Вы были замечены во время кражи.')
}
Exp(ctx, character, character.intelligence + 7)
generateCard(ctx.from.id)
let newCard = generateCard(ctx.from.id)
logs(ctx, "Карманная кража (Карта)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, card: newCard});
character.save()
return ctx.editMessageText(`Вы успешно украли 💳 из бумажника.`)
});
@ -820,6 +825,7 @@ rpg.action(`POCKET_BAG`, async (ctx) => {
}
Exp(ctx, character, character.intelligence + times)
character.dirtymoney += moneyIn
logs(ctx, "Карманная кража (Сумка)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, moneyIn: moneyIn});
character.save()
return ctx.editMessageText(`Вы успешно украли сумку и сбыли все ценности из нее:\n${text}\nОбщий куш: Ð${utils.spaces(moneyIn)}`)
});
@ -848,6 +854,7 @@ rpg.action('SHOP_ACTION', async (ctx) => {
if (character.shoprobcd > cooldown.currentTime) return ctx.editMessageText(`📛 Данное действие будет доступно через ${cooldown.timeLeftInMinutes} мин.`);
character.shoprobcd = cooldown.endTime
character.stamina -= 25
logs(ctx, "Ограбление магазина (Start)", {cdBefore: shoprobcd, cdAfter: character.shoprobcd, stamina: character.stamina});
character.save()
return ctx.editMessageText('Стадии:', Markup.inlineKeyboard([
[{ text: 'Взлом кассы', callback_data: `SHOP_CASH_BREAK` }],
@ -865,6 +872,7 @@ rpg.action(`SHOP_CASH_BREAK`, async (ctx) => {
if (chance < randomRoll) {
const keyboard = generateKeyboard();
ctx.deleteMessage()
logs(ctx, "Ограбление магазина (Взлом кассы ручной)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, keyboard: keyboard, cashIn: cashIn});
return ctx.reply('Касса закрыта, вы начали взлом замка:', keyboard);
//ctx.editMessageText('Вы начали взлом кассы.');
//return ctx.scene.enter('LOCKPICK')
@ -884,6 +892,7 @@ rpg.action(`SHOP_CASH_BREAK`, async (ctx) => {
[{ text: 'Завершить ограбление', callback_data: `SHOP_END` }]
]))
}, timer + 300)
logs(ctx, "Ограбление магазина (Взлом кассы быстро)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, cashIn: cashIn});
});
rpg.action(`SHOP_CASH_SMASH`, async (ctx) => {
@ -913,6 +922,7 @@ rpg.action(`SHOP_CASH_SMASH`, async (ctx) => {
[{ text: 'Завершить ограбление', callback_data: `SHOP_END` }]
]))
}, timer + 300)
logs(ctx, "Ограбление магазина (Разбитие кассы)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, cashIn: cashIn});
});
rpg.action(`SHOP_CASH_BREAK_SUCCESS`, async (ctx) => {
@ -936,6 +946,7 @@ rpg.action(`SHOP_CASH_BREAK_SUCCESS`, async (ctx) => {
[{ text: 'Завершить ограбление', callback_data: `SHOP_END` }]
]))
}, timer + 300)
logs(ctx, "Ограбление магазина (Взлом кассы)", {cashIn: cashIn});
});
rpg.action(`SHOP_END`, async (ctx) => {
@ -959,9 +970,10 @@ rpg.action(/lock_*/, async (ctx) => {
// Проверяем, не исчерпаны ли попытки
if (attempts[userId] >= 5) {
delete attempts[userId]; // Сбрасываем попытки после провала
logs(ctx, "Взлом замка (Full Fail)", {attempts: attempts[userId]});
return ctx.editMessageText('Взлом провалился. Замок остается нетронутым.');
}
logs(ctx, "Взлом замка (Fail)", {attempts: attempts[userId]});
// Ответ на нажатие кнопки
ctx.answerCbQuery(`Штифт не сдвинулся. Попыток осталось: ${5 - attempts[userId]}`);
removeButton(ctx, buttonId);
@ -1014,7 +1026,6 @@ rpg.action('inventory', async (ctx) => {
),
];
buttons.push(Markup.button.callback('🔙 В меню', 'crime_menu'));
await ctx.editMessageText(message, Markup.inlineKeyboard(buttons, { columns: 2 }));
} catch (error) {
console.error('Ошибка при выводе инвентаря:', error);
@ -1048,8 +1059,8 @@ rpg.action(/view_item_(\d+)/, async (ctx) => {
if (item.equipped && item.canBeEquipped) {
buttons.push([Markup.button.callback(`🚫 Снять ${item.name}`, `unequip_item_${item.id}`)]);
}
await ctx.replyWithMarkdown(message, Markup.inlineKeyboard(buttons, { columns: 2 }));
logs(ctx, "Просмотр предмета", {item: item});
await ctx.reply(message, Markup.inlineKeyboard(buttons, { columns: 2 }));
});
rpg.action(/use_item_(\d+)/, async (ctx) => {
@ -1521,32 +1532,6 @@ const fillEnemies = async () => {
console.log("Enemies filled.");
};
const fillLocations = async () => {
const locations = [
{
name: "Заброшенный склад",
description: "Темный склад с кучей мусора.",
enemies: [1, 2, 3],
level: 1,
loot: [6, 7],
rarity: 1
},
{
name: "Лесопосадка",
description: "Густой лес с опасными тропами.",
enemies: [2, 3, 4],
level: 2,
loot: [8, 9],
rarity: 2
},
];
for (const location of locations) {
await Location.create(location);
}
console.log("Locations filled.");
};
const generateBattles = async () => {
const locations = await Location.findAll();
@ -1592,14 +1577,17 @@ const generateBattles = async () => {
generateBattles()
schedule.scheduleJob('0 * * * *', generateBattles); // Каждый час в начале часа
rpg.command("locations", async (ctx) => {
rpg.action("locations", async (ctx) => {
const locations = await Location.findAll();
if (!locations.length) {
return ctx.reply("Нет доступных локаций для исследования.");
}
const locationButtons = locations.map((location) => ({
text: location.name,
text: location.name + ` (${location.level} lvl.)`,
callback_data: `viewlocation_${location.id}`,
}));
@ -1628,7 +1616,7 @@ rpg.action(/viewlocation_\d+/, async (ctx) => {
[{ text: "Охота", callback_data: `hunt_location_${location.id}` }],
]);
await ctx.reply(description, keyboard);
await ctx.editMessageText(description, keyboard);
});
rpg.action(/hunt_location_\d+/, async (ctx) => {
@ -1643,7 +1631,7 @@ rpg.action(/hunt_location_\d+/, async (ctx) => {
if (!character) {
return ctx.reply("Ваш персонаж не найден.");
}
logs(ctx, "Охота", {location: location});
const inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } });
const hasGrenadeLow = inventory.some((item) => item.text_id === "grenade_low");
const hasGrenadeMedium = inventory.some((item) => item.text_id === "grenade_medium");
@ -1651,7 +1639,7 @@ rpg.action(/hunt_location_\d+/, async (ctx) => {
const enemyIds = location.enemies || [];
if (enemyIds.length === 0) {
return ctx.reply("В этой локации сейчас нет врагов. Попробуйте позже.");
return ctx.answerCbQuery("В этой локации сейчас нет врагов. Попробуйте позже.");
}
const activeBattles = await Battle.findAll({
@ -1659,14 +1647,14 @@ rpg.action(/hunt_location_\d+/, async (ctx) => {
});
if (activeBattles.length === 0) {
return ctx.reply("В этой локации сейчас нет активных врагов. Попробуйте позже.");
return ctx.answerCbQuery("В этой локации сейчас нет активных врагов. Попробуйте позже.");
}
const activeEnemyIds = activeBattles.map((battle) => battle.enemy);
const enemies = await Enemy.findAll({ where: { id: activeEnemyIds } });
if (enemies.length === 0) {
return ctx.reply("В этой локации сейчас нет врагов.");
return ctx.answerCbQuery("В этой локации сейчас нет врагов.");
}
const buttons = activeBattles.map((battle) => {
@ -1686,7 +1674,7 @@ rpg.action(/hunt_location_\d+/, async (ctx) => {
const keyboard = Markup.inlineKeyboard(buttons.map((btn) => [btn]));
await ctx.reply(
await ctx.editMessageText(
`🔍 Локация: ${location.name}\n\nВыберите врага для охоты:`,
keyboard
);
@ -1749,7 +1737,7 @@ rpg.action(/use_grenade_\d+/, async (ctx) => {
// Удаляем гранату из инвентаря
await InventoryModel.destroy({ where: { id: grenade.id } });
await ctx.reply(message);
await ctx.answerCbQuery(message, { show_alert: true });
});
@ -1920,20 +1908,20 @@ function generateBattleButtons(character, battle) {
// Генерация кнопки с критическим уроном
buttons.push({
text: `💥`,
text: `🎯`,
callback_data: `critical_${battle.id}`,
});
// Гарантированная кнопка промаха
buttons.push({
text: "",
text: "🎯",
callback_data: `miss_${battle.id}`,
});
// Заполнение оставшихся кнопок (если не хватает до totalButtons)
while (buttons.length < totalButtons) {
buttons.push({
text: "",
text: "🎯",
callback_data: `miss_${battle.id}`,
});
}
@ -2055,40 +2043,4 @@ rpg.action(/miss_\d+/, async (ctx) => {
const fillItemsTable = async () => {
try {
const items = [];
// Генерируем 10 случайных предметов
for (let i = 0; i < 10; i++) {
const item = {
name: fakerRU.commerce.productName(), // Название товара
description: fakerRU.lorem.sentence(), // Описание товара
price: fakerRU.commerce.price({ dec: 0 }), // Цена товара
rarity: 1, // Редкость (от 1 до 5)
type: 'other', // Тип предмета
};
items.push(item);
}
// Вставляем все 10 предметов в таблицу
await ItemsModel.bulkCreate(items);
console.log('Таблица предметов успешно заполнена!');
} catch (error) {
console.error('Ошибка при заполнении таблицы предметов:', error);
}
};
// Запускаем функцию
//fillItemsTable();
module.exports = rpg;

52
utils/logMiddleware.js Normal file
View File

@ -0,0 +1,52 @@
const fs = require("fs");
/**
* Middleware для логирования всех действий в боте.
*/
function logMiddleware(ctx, next) {
const logEntry = {
timestamp: new Date().toISOString(),
user: {
id: ctx.from?.id || "unknown",
username: ctx.from?.username || "unknown",
first_name: ctx.from?.first_name || "unknown",
last_name: ctx.from?.last_name || "",
},
chat: ctx.chat ? { id: ctx.chat.id, type: ctx.chat.type } : null,
type: ctx.updateType, // Тип обновления (message, callback_query и т.д.)
data: getDataFromContext(ctx), // Извлечение данных из контекста
};
const logString = JSON.stringify(logEntry, null, 2);
// Сохраняем лог в файл
fs.appendFile('./json/logs.json', logString + "\n", (err) => {
if (err) {
console.error("Ошибка при записи лога:", err);
}
});
// Вызываем следующий middleware
return next();
}
/**
* Извлечение данных из контекста (например, текст сообщения или данные кнопки).
*/
function getDataFromContext(ctx) {
if (ctx.message) {
return { text: ctx.message.text || "нет текста", message_id: ctx.message.message_id };
}
if (ctx.callbackQuery) {
return { data: ctx.callbackQuery.data || "нет данных", id: ctx.callbackQuery.id };
}
if (ctx.inlineQuery) {
return { query: ctx.inlineQuery.query || "пустой запрос" };
}
return "неизвестное событие";
}
module.exports = logMiddleware;

34
utils/logs.js Normal file
View File

@ -0,0 +1,34 @@
const fs = require("fs");
const path = require("path");
/**
* Функция для записи логов.
* @param {Object} ctx - Контекст Telegraf (содержит данные о пользователе и сообщении).
* @param {String} action - Описание действия, например, "Начало битвы", "Использована граната".
* @param {Object} [extraData] - Дополнительные данные для логов.
*/
function logAction(ctx, action, extraData = {}) {
const logEntry = {
timestamp: new Date().toISOString(),
user: {
id: ctx.from.id,
username: ctx.from.username || "unknown",
first_name: ctx.from.first_name || "unknown",
last_name: ctx.from.last_name || "",
},
chat: ctx.chat ? { id: ctx.chat.id, type: ctx.chat.type } : null,
action,
extraData,
};
const logString = JSON.stringify(logEntry, null, 2);
// Сохраняем лог в файл
fs.appendFile('./json/logs.json', logString + "\n", (err) => {
if (err) {
console.error("Ошибка при записи лога:", err);
}
});
}
module.exports = logAction;