first commit

This commit is contained in:
2025-07-21 00:45:28 +02:00
parent ab10b0f9a1
commit 93232a1663
39 changed files with 4860 additions and 0 deletions

View File

@@ -0,0 +1,440 @@
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
class SlashCommands {
constructor(database) {
this.db = database;
this.commands = this.setupCommands();
}
setupCommands() {
return [
// Komenda informacyjna dla użytkowników
new SlashCommandBuilder()
.setName('skrzynka')
.setDescription('Informacje o bocie Skrzynka Impostora')
.addSubcommand(subcommand =>
subcommand
.setName('info')
.setDescription('Pokaż informacje o bocie')
)
.addSubcommand(subcommand =>
subcommand
.setName('help')
.setDescription('Pokaż dostępne komendy')
),
// Komendy administracyjne
new SlashCommandBuilder()
.setName('skrzynka-adm')
.setDescription('Komendy administracyjne dla bota Skrzynka Impostora')
.setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels)
.addSubcommand(subcommand =>
subcommand
.setName('set-welcome')
.setDescription('Ustaw kanał powitalny')
.addChannelOption(option =>
option
.setName('channel')
.setDescription('Kanał na którym będzie wyświetlana wiadomość powitalna')
.setRequired(true)
)
)
.addSubcommand(subcommand =>
subcommand
.setName('welcome')
.setDescription('Wyślij wiadomość powitalną')
)
.addSubcommand(subcommand =>
subcommand
.setName('welcome-update')
.setDescription('Zaktualizuj wiadomość powitalną z bazy danych')
)
.addSubcommand(subcommand =>
subcommand
.setName('status')
.setDescription('Pokaż status konfiguracji bota')
)
];
}
getCommandsData() {
return this.commands.map(command => command.toJSON());
}
async handleCommand(interaction) {
const { commandName, options } = interaction;
switch (commandName) {
case 'skrzynka':
await this.handleUserCommands(interaction, options);
break;
case 'skrzynka-adm':
await this.handleAdminCommands(interaction, options);
break;
default:
await interaction.reply({
content: '❌ Nieznana komenda.',
ephemeral: true
});
}
}
async handleUserCommands(interaction, options) {
const subcommand = options.getSubcommand();
switch (subcommand) {
case 'info':
await this.showBotInfo(interaction);
break;
case 'help':
await this.showHelp(interaction);
break;
}
}
async handleAdminCommands(interaction, options) {
const subcommand = options.getSubcommand();
// Sprawdź uprawnienia użytkownika
if (!interaction.member.permissions.has(PermissionFlagsBits.ManageChannels)) {
await interaction.reply({
content: '❌ Nie masz uprawnień do używania komend administracyjnych.',
ephemeral: true
});
return;
}
switch (subcommand) {
case 'set-welcome':
await this.setWelcomeChannel(interaction, options);
break;
case 'welcome':
await this.sendWelcomeMessage(interaction);
break;
case 'welcome-update':
await this.updateWelcomeMessage(interaction);
break;
case 'status':
await this.showStatus(interaction);
break;
}
}
async showBotInfo(interaction) {
const embed = new EmbedBuilder()
.setColor(0x00AE86)
.setTitle('🎭 Skrzynka Impostora Bot')
.setDescription('Bot do zarządzania wiadomościami powaitalnymi na serwerze Discord.')
.addFields(
{ name: '📝 Wersja', value: '1.0.0', inline: true },
{ name: '🔧 Panel Web', value: 'Dostępny dla administratorów', inline: true },
{ name: '⚡ Status', value: 'Online', inline: true },
{ name: '💻 Funkcje', value: '• Automatyczne wiadomości powitalne\n• Panel zarządzania web\n• Historia zmian\n• Konfiguracja kanałów', inline: false }
)
.setThumbnail(interaction.client.user.displayAvatarURL())
.setFooter({
text: 'Skrzynka Impostora Bot',
iconURL: interaction.client.user.displayAvatarURL()
})
.setTimestamp();
await interaction.reply({ embeds: [embed] });
}
async showHelp(interaction) {
const embed = new EmbedBuilder()
.setColor(0x0099FF)
.setTitle('📚 Pomoc - Dostępne komendy')
.addFields(
{
name: '👥 Komendy użytkownika',
value: '`/skrzynka info` - Informacje o bocie\n`/skrzynka help` - Ta pomoc',
inline: false
},
{
name: '🛠️ Komendy administracyjne',
value: '`/skrzynka-adm set-welcome` - Ustaw kanał powitalny\n`/skrzynka-adm welcome` - Wyślij wiadomość powitalną\n`/skrzynka-adm welcome-update` - Zaktualizuj wiadomość\n`/skrzynka-adm status` - Status konfiguracji',
inline: false
},
{
name: '🌐 Panel Web',
value: 'Zarządzanie treścią wiadomości dostępne przez panel web.',
inline: false
}
)
.setFooter({
text: 'Potrzebujesz pomocy? Skontaktuj się z administratorem serwera.',
iconURL: interaction.client.user.displayAvatarURL()
})
.setTimestamp();
await interaction.reply({ embeds: [embed], ephemeral: true });
}
async setWelcomeChannel(interaction, options) {
const channel = options.getChannel('channel');
const guildId = interaction.guild.id;
const guildName = interaction.guild.name;
try {
// Sprawdź czy bot ma uprawnienia do pisania na kanale
if (!channel.permissionsFor(interaction.client.user).has(PermissionFlagsBits.SendMessages)) {
await interaction.reply({
content: '❌ Bot nie ma uprawnień do pisania na wybranym kanale.',
ephemeral: true
});
return;
}
// Dodaj/zaktualizuj serwer w bazie
await this.db.addGuild(guildId, guildName);
// Ustaw kanał powitalny
await this.db.setWelcomeChannel(guildId, channel.id, channel.name);
const embed = new EmbedBuilder()
.setColor(0x00FF00)
.setTitle('✅ Kanał powitalny ustawiony')
.setDescription(`Kanał ${channel} został ustawiony jako kanał powitalny.`)
.addFields(
{ name: 'Kanał', value: `${channel.name} (${channel.id})`, inline: false },
{ name: 'Następne kroki', value: 'Skonfiguruj treść wiadomości przez panel web lub użyj `/skrzynka-adm welcome` aby wysłać domyślną wiadomość.', inline: false }
)
.setTimestamp();
await interaction.reply({ embeds: [embed] });
} catch (error) {
console.error('Błąd podczas ustawiania kanału powitalnego:', error);
await interaction.reply({
content: '❌ Wystąpił błąd podczas ustawiania kanału powitalnego.',
ephemeral: true
});
}
}
async sendWelcomeMessage(interaction) {
const guildId = interaction.guild.id;
try {
await interaction.deferReply({ ephemeral: true });
// Pobierz konfigurację kanału
const welcomeChannelConfig = await this.db.getWelcomeChannel(guildId);
if (!welcomeChannelConfig) {
await interaction.editReply({
content: '❌ Kanał powitalny nie został skonfigurowany. Użyj `/skrzynka-adm set-welcome` aby go ustawić.'
});
return;
}
// Pobierz kanał
const channel = interaction.guild.channels.cache.get(welcomeChannelConfig.channel_id);
if (!channel) {
await interaction.editReply({
content: '❌ Skonfigurowany kanał powitalny nie istnieje lub bot nie ma do niego dostępu.'
});
return;
}
// Pobierz wiadomość z bazy lub użyj domyślnej
let welcomeMessage = await this.db.getWelcomeMessage(guildId);
let messageContent, embedData;
if (welcomeMessage) {
messageContent = welcomeMessage.content;
embedData = welcomeMessage.embed_data;
} else {
messageContent = this.getDefaultWelcomeMessage(interaction.guild);
embedData = null;
}
// Wyślij wiadomość
const messageOptions = { content: messageContent };
if (embedData) {
messageOptions.embeds = [new EmbedBuilder(embedData)];
}
const sentMessage = await channel.send(messageOptions);
// Zapisz lub zaktualizuj wiadomość w bazie
if (welcomeMessage) {
await this.db.updateWelcomeMessage(guildId, messageContent, embedData, sentMessage.id);
} else {
await this.db.saveWelcomeMessage(guildId, messageContent, embedData, sentMessage.id);
}
const successEmbed = new EmbedBuilder()
.setColor(0x00FF00)
.setTitle('✅ Wiadomość powitalna wysłana')
.setDescription(`Wiadomość została wysłana na kanał ${channel}.`)
.addFields(
{ name: 'Kanał', value: `${channel.name}`, inline: true },
{ name: 'ID wiadomości', value: `${sentMessage.id}`, inline: true }
)
.setTimestamp();
await interaction.editReply({ embeds: [successEmbed] });
} catch (error) {
console.error('Błąd podczas wysyłania wiadomości powitalnej:', error);
await interaction.editReply({
content: '❌ Wystąpił błąd podczas wysyłania wiadomości powitalnej.'
});
}
}
async updateWelcomeMessage(interaction) {
const guildId = interaction.guild.id;
try {
await interaction.deferReply({ ephemeral: true });
// Pobierz konfigurację i wiadomość
const welcomeChannelConfig = await this.db.getWelcomeChannel(guildId);
const welcomeMessage = await this.db.getWelcomeMessage(guildId);
if (!welcomeChannelConfig || !welcomeMessage) {
await interaction.editReply({
content: '❌ Kanał powitalny lub wiadomość nie zostały skonfigurowane.'
});
return;
}
const channel = interaction.guild.channels.cache.get(welcomeChannelConfig.channel_id);
if (!channel) {
await interaction.editReply({
content: '❌ Skonfigurowany kanał powitalny nie istnieje.'
});
return;
}
// Znajdź i zaktualizuj wiadomość
if (welcomeMessage.message_id) {
try {
const message = await channel.messages.fetch(welcomeMessage.message_id);
const messageOptions = { content: welcomeMessage.content };
if (welcomeMessage.embed_data) {
messageOptions.embeds = [new EmbedBuilder(welcomeMessage.embed_data)];
}
await message.edit(messageOptions);
const successEmbed = new EmbedBuilder()
.setColor(0x00FF00)
.setTitle('✅ Wiadomość zaktualizowana')
.setDescription(`Wiadomość powitalna została zaktualizowana na kanale ${channel}.`)
.setTimestamp();
await interaction.editReply({ embeds: [successEmbed] });
} catch (fetchError) {
// Jeśli nie można znaleźć wiadomości, wyślij nową
await this.sendWelcomeMessage(interaction);
}
} else {
// Jeśli nie ma ID wiadomości, wyślij nową
await this.sendWelcomeMessage(interaction);
}
} catch (error) {
console.error('Błąd podczas aktualizacji wiadomości powitalnej:', error);
await interaction.editReply({
content: '❌ Wystąpił błąd podczas aktualizacji wiadomości powitalnej.'
});
}
}
async showStatus(interaction) {
const guildId = interaction.guild.id;
try {
const welcomeChannelConfig = await this.db.getWelcomeChannel(guildId);
const welcomeMessage = await this.db.getWelcomeMessage(guildId);
const embed = new EmbedBuilder()
.setColor(0x0099FF)
.setTitle('📊 Status konfiguracji bota')
.setDescription(`Status dla serwera: **${interaction.guild.name}**`);
if (welcomeChannelConfig) {
const channel = interaction.guild.channels.cache.get(welcomeChannelConfig.channel_id);
embed.addFields({
name: '📍 Kanał powitalny',
value: channel ? `${channel.name} (${channel.id})` : `❌ Kanał niedostępny (${welcomeChannelConfig.channel_id})`,
inline: false
});
} else {
embed.addFields({
name: '📍 Kanał powitalny',
value: '❌ Nie skonfigurowany',
inline: false
});
}
if (welcomeMessage) {
embed.addFields(
{
name: '💬 Wiadomość powitalna',
value: '✅ Skonfigurowana w bazie danych',
inline: true
},
{
name: '🆔 ID wiadomości',
value: welcomeMessage.message_id || 'Brak',
inline: true
},
{
name: '📅 Ostatnia aktualizacja',
value: new Date(welcomeMessage.updated_at).toLocaleString('pl-PL'),
inline: true
}
);
} else {
embed.addFields({
name: '💬 Wiadomość powitalna',
value: '❌ Nie skonfigurowana (będzie użyta domyślna)',
inline: false
});
}
embed.setFooter({
text: 'Użyj panelu web aby zarządzać treścią wiadomości',
iconURL: interaction.client.user.displayAvatarURL()
})
.setTimestamp();
await interaction.reply({ embeds: [embed], ephemeral: true });
} catch (error) {
console.error('Błąd podczas pobierania statusu:', error);
await interaction.reply({
content: '❌ Wystąpił błąd podczas pobierania statusu.',
ephemeral: true
});
}
}
getDefaultWelcomeMessage(guild) {
return `# 🎭 Witamy na serwerze ${guild.name}!
Miło Cię tutaj widzieć! 👋
## 📋 Najważniejsze informacje:
• Przeczytaj regulamin serwera
• Przedstaw się w odpowiednim kanale
• Baw się dobrze i szanuj innych członków
## 🎮 Funkcje serwera:
• Kanały tematyczne
• System ról
• Eventy i konkursy
---
*Ta wiadomość została wygenerowana przez Skrzynka Impostora Bot*
*Administratorzy mogą edytować treść przez panel web*`;
}
}
module.exports = { SlashCommands };

View File

@@ -0,0 +1,234 @@
const { Pool } = require('pg');
require('dotenv').config();
class DatabaseManager {
constructor() {
this.pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
});
}
async initialize() {
try {
await this.createTables();
console.log('Tabele bazy danych utworzone pomyślnie');
} catch (error) {
console.error('Błąd podczas inicjalizacji bazy danych:', error);
throw error;
}
}
async createTables() {
const createTablesSQL = `
-- Tabela serwerów Discord
CREATE TABLE IF NOT EXISTS guilds (
id VARCHAR(20) PRIMARY KEY,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Tabela kanałów powitalnych
CREATE TABLE IF NOT EXISTS welcome_channels (
id SERIAL PRIMARY KEY,
guild_id VARCHAR(20) REFERENCES guilds(id) ON DELETE CASCADE,
channel_id VARCHAR(20) NOT NULL,
channel_name VARCHAR(100),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(guild_id)
);
-- Tabela wiadomości powitalnych
CREATE TABLE IF NOT EXISTS welcome_messages (
id SERIAL PRIMARY KEY,
guild_id VARCHAR(20) REFERENCES guilds(id) ON DELETE CASCADE,
content TEXT NOT NULL,
embed_data JSONB,
message_id VARCHAR(20),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Tabela użytkowników panelu web
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
discord_id VARCHAR(20) UNIQUE NOT NULL,
username VARCHAR(32) NOT NULL,
discriminator VARCHAR(4),
avatar VARCHAR(255),
email VARCHAR(255),
role VARCHAR(20) DEFAULT 'viewer',
is_active BOOLEAN DEFAULT true,
last_login TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Tabela historii wersji wiadomości
CREATE TABLE IF NOT EXISTS message_revisions (
id SERIAL PRIMARY KEY,
message_id INTEGER REFERENCES welcome_messages(id) ON DELETE CASCADE,
content TEXT NOT NULL,
embed_data JSONB,
user_id INTEGER REFERENCES users(id),
revision_number INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Tabela uprawnień użytkowników do serwerów
CREATE TABLE IF NOT EXISTS user_guild_permissions (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
guild_id VARCHAR(20) REFERENCES guilds(id) ON DELETE CASCADE,
role VARCHAR(20) DEFAULT 'viewer',
granted_by INTEGER REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, guild_id)
);
-- Indeksy dla wydajności
CREATE INDEX IF NOT EXISTS idx_guilds_id ON guilds(id);
CREATE INDEX IF NOT EXISTS idx_welcome_channels_guild_id ON welcome_channels(guild_id);
CREATE INDEX IF NOT EXISTS idx_welcome_messages_guild_id ON welcome_messages(guild_id);
CREATE INDEX IF NOT EXISTS idx_users_discord_id ON users(discord_id);
CREATE INDEX IF NOT EXISTS idx_message_revisions_message_id ON message_revisions(message_id);
CREATE INDEX IF NOT EXISTS idx_user_guild_permissions_user_guild ON user_guild_permissions(user_id, guild_id);
`;
await this.pool.query(createTablesSQL);
}
// Metody dla zarządzania serwerami
async addGuild(guildId, guildName) {
const query = `
INSERT INTO guilds (id, name)
VALUES ($1, $2)
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
updated_at = CURRENT_TIMESTAMP
`;
await this.pool.query(query, [guildId, guildName]);
}
async getGuild(guildId) {
const query = 'SELECT * FROM guilds WHERE id = $1';
const result = await this.pool.query(query, [guildId]);
return result.rows[0];
}
// Metody dla kanałów powitalnych
async setWelcomeChannel(guildId, channelId, channelName) {
const query = `
INSERT INTO welcome_channels (guild_id, channel_id, channel_name)
VALUES ($1, $2, $3)
ON CONFLICT (guild_id) DO UPDATE SET
channel_id = EXCLUDED.channel_id,
channel_name = EXCLUDED.channel_name,
is_active = true,
updated_at = CURRENT_TIMESTAMP
`;
await this.pool.query(query, [guildId, channelId, channelName]);
}
async getWelcomeChannel(guildId) {
const query = 'SELECT * FROM welcome_channels WHERE guild_id = $1 AND is_active = true';
const result = await this.pool.query(query, [guildId]);
return result.rows[0];
}
// Metody dla wiadomości powitalnych
async saveWelcomeMessage(guildId, content, embedData = null, messageId = null) {
const query = `
INSERT INTO welcome_messages (guild_id, content, embed_data, message_id)
VALUES ($1, $2, $3, $4)
RETURNING *
`;
const result = await this.pool.query(query, [guildId, content, embedData, messageId]);
return result.rows[0];
}
async updateWelcomeMessage(guildId, content, embedData = null, messageId = null) {
const query = `
UPDATE welcome_messages
SET content = $2, embed_data = $3, message_id = $4, updated_at = CURRENT_TIMESTAMP
WHERE guild_id = $1 AND is_active = true
RETURNING *
`;
const result = await this.pool.query(query, [guildId, content, embedData, messageId]);
return result.rows[0];
}
async getWelcomeMessage(guildId) {
const query = 'SELECT * FROM welcome_messages WHERE guild_id = $1 AND is_active = true ORDER BY created_at DESC LIMIT 1';
const result = await this.pool.query(query, [guildId]);
return result.rows[0];
}
// Metody dla użytkowników
async saveUser(discordUser) {
const query = `
INSERT INTO users (discord_id, username, discriminator, avatar, email)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (discord_id) DO UPDATE SET
username = EXCLUDED.username,
discriminator = EXCLUDED.discriminator,
avatar = EXCLUDED.avatar,
email = EXCLUDED.email,
last_login = CURRENT_TIMESTAMP,
updated_at = CURRENT_TIMESTAMP
RETURNING *
`;
const result = await this.pool.query(query, [
discordUser.id,
discordUser.username,
discordUser.discriminator || '0000',
discordUser.avatar,
discordUser.email
]);
return result.rows[0];
}
async getUser(discordId) {
const query = 'SELECT * FROM users WHERE discord_id = $1';
const result = await this.pool.query(query, [discordId]);
return result.rows[0];
}
// Metody dla historii wersji
async saveMessageRevision(messageId, content, embedData, userId) {
// Pobierz aktualny numer rewizji
const revisionQuery = 'SELECT COALESCE(MAX(revision_number), 0) + 1 as next_revision FROM message_revisions WHERE message_id = $1';
const revisionResult = await this.pool.query(revisionQuery, [messageId]);
const nextRevision = revisionResult.rows[0].next_revision;
const query = `
INSERT INTO message_revisions (message_id, content, embed_data, user_id, revision_number)
VALUES ($1, $2, $3, $4, $5)
RETURNING *
`;
const result = await this.pool.query(query, [messageId, content, embedData, userId, nextRevision]);
return result.rows[0];
}
async getMessageRevisions(messageId) {
const query = `
SELECT mr.*, u.username
FROM message_revisions mr
LEFT JOIN users u ON mr.user_id = u.id
WHERE mr.message_id = $1
ORDER BY mr.revision_number DESC
`;
const result = await this.pool.query(query, [messageId]);
return result.rows;
}
async close() {
await this.pool.end();
}
}
module.exports = { DatabaseManager };

View File

@@ -0,0 +1,43 @@
const { REST, Routes } = require('discord.js');
const { SlashCommands } = require('./commands');
require('dotenv').config();
async function deployCommands() {
if (!process.env.DISCORD_TOKEN || !process.env.DISCORD_CLIENT_ID) {
console.error('❌ Brak wymaganych zmiennych środowiskowych (DISCORD_TOKEN, DISCORD_CLIENT_ID)');
process.exit(1);
}
const commands = new SlashCommands();
const commandsData = commands.getCommandsData();
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
try {
console.log(`🚀 Rozpoczęto deploy ${commandsData.length} komend slash...`);
// Deploy globalnych komend
const data = await rest.put(
Routes.applicationCommands(process.env.DISCORD_CLIENT_ID),
{ body: commandsData }
);
console.log(`✅ Pomyślnie zdeployowano ${data.length} komend slash globalnie.`);
// Wyświetl listę komend
console.log('\n📋 Zdeployowane komendy:');
data.forEach(command => {
console.log(` • /${command.name} - ${command.description}`);
});
} catch (error) {
console.error('❌ Błąd podczas deployowania komend:', error);
}
}
// Jeśli skrypt jest uruchamiany bezpośrednio
if (require.main === module) {
deployCommands();
}
module.exports = { deployCommands };

113
bot/backend/index.js Normal file
View File

@@ -0,0 +1,113 @@
const { Client, GatewayIntentBits, REST, Routes, EmbedBuilder } = require('discord.js');
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');
require('dotenv').config();
const { DatabaseManager } = require('./database/DatabaseManager');
const { SlashCommands } = require('./commands');
const { WebPanel } = require('./web/server');
class SkrzynkaImpostoraBot {
constructor() {
this.client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMembers
]
});
this.db = new DatabaseManager();
this.commands = new SlashCommands(this.db);
this.webPanel = new WebPanel(this.db);
this.setupEventHandlers();
}
setupEventHandlers() {
this.client.once('ready', () => {
console.log(`✅ Bot zalogowany jako ${this.client.user.tag}`);
this.client.user.setActivity('Zarządzanie wiadomościami powiatalnymi', { type: 'WATCHING' });
});
this.client.on('guildCreate', async (guild) => {
console.log(`Dodano do nowego serwera: ${guild.name} (${guild.id})`);
await this.db.addGuild(guild.id, guild.name);
});
this.client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
try {
await this.commands.handleCommand(interaction);
} catch (error) {
console.error('Błąd podczas wykonywania komendy:', error);
const errorEmbed = new EmbedBuilder()
.setColor(0xFF0000)
.setTitle('❌ Błąd')
.setDescription('Wystąpił błąd podczas wykonywania komendy.')
.setTimestamp();
if (interaction.replied || interaction.deferred) {
await interaction.followUp({ embeds: [errorEmbed], ephemeral: true });
} else {
await interaction.reply({ embeds: [errorEmbed], ephemeral: true });
}
}
});
this.client.on('error', console.error);
}
async start() {
try {
// Inicjalizacja bazy danych
await this.db.initialize();
console.log('✅ Baza danych zainicjalizowana');
// Uruchomienie panelu web
await this.webPanel.start();
console.log('✅ Panel web uruchomiony');
// Logowanie bota
await this.client.login(process.env.DISCORD_TOKEN);
console.log('✅ Bot Discord uruchomiony');
} catch (error) {
console.error('❌ Błąd podczas uruchamiania bota:', error);
process.exit(1);
}
}
async deployCommands() {
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
try {
console.log('Rozpoczęto odświeżanie komend slash...');
const commands = this.commands.getCommandsData();
await rest.put(
Routes.applicationCommands(process.env.DISCORD_CLIENT_ID),
{ body: commands }
);
console.log('✅ Komendy slash zostały pomyślnie odświeżone.');
} catch (error) {
console.error('❌ Błąd podczas odświeżania komend:', error);
}
}
}
// Uruchomienie bota
if (require.main === module) {
const bot = new SkrzynkaImpostoraBot();
bot.start();
}
module.exports = { SkrzynkaImpostoraBot };

368
bot/backend/web/server.js Normal file
View File

@@ -0,0 +1,368 @@
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');
const jwt = require('jsonwebtoken');
const { body, validationResult } = require('express-validator');
require('dotenv').config();
class WebPanel {
constructor(database) {
this.db = database;
this.app = express();
this.setupMiddleware();
this.setupRoutes();
}
setupMiddleware() {
// Bezpieczeństwo
this.app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
scriptSrc: ["'self'"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://discord.com", "https://discordapp.com"]
}
}
}));
// CORS
this.app.use(cors({
origin: process.env.NODE_ENV === 'production'
? ['https://your-domain.com']
: ['http://localhost:3001', 'http://127.0.0.1:3001'],
credentials: true
}));
// Middleware podstawowe
this.app.use(compression());
this.app.use(morgan('combined'));
this.app.use(express.json({ limit: '10mb' }));
this.app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Serwowanie plików statycznych (dla built React app)
this.app.use(express.static('frontend/build'));
}
setupRoutes() {
// API Routes
this.app.use('/api/auth', this.createAuthRoutes());
this.app.use('/api/guilds', this.createGuildRoutes());
this.app.use('/api/messages', this.createMessageRoutes());
// Health check
this.app.get('/api/health', (req, res) => {
res.json({
status: 'OK',
timestamp: new Date().toISOString(),
version: '1.0.0'
});
});
// Catch all handler dla React Router
this.app.get('*', (req, res) => {
res.sendFile('index.html', { root: 'frontend/build' });
});
// Error handler
this.app.use(this.errorHandler.bind(this));
}
createAuthRoutes() {
const router = express.Router();
// Discord OAuth2 login
router.get('/discord', (req, res) => {
const discordAuthUrl = `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT_ID}&redirect_uri=${encodeURIComponent(process.env.OAUTH2_REDIRECT_URI)}&response_type=code&scope=identify%20guilds`;
res.redirect(discordAuthUrl);
});
// Discord OAuth2 callback
router.get('/discord/callback', async (req, res) => {
const { code } = req.query;
if (!code) {
return res.status(400).json({ error: 'Brak kodu autoryzacji' });
}
try {
// Wymiana kodu na token
const tokenResponse = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
client_id: process.env.DISCORD_CLIENT_ID,
client_secret: process.env.DISCORD_CLIENT_SECRET,
code,
grant_type: 'authorization_code',
redirect_uri: process.env.OAUTH2_REDIRECT_URI,
}),
});
const tokenData = await tokenResponse.json();
if (!tokenData.access_token) {
throw new Error('Nie udało się uzyskać tokenu dostępu');
}
// Pobierz dane użytkownika
const userResponse = await fetch('https://discord.com/api/users/@me', {
headers: {
Authorization: `Bearer ${tokenData.access_token}`,
},
});
const userData = await userResponse.json();
// Pobierz serwery użytkownika
const guildsResponse = await fetch('https://discord.com/api/users/@me/guilds', {
headers: {
Authorization: `Bearer ${tokenData.access_token}`,
},
});
const guildsData = await guildsResponse.json();
// Zapisz użytkownika w bazie
const user = await this.db.saveUser(userData);
// Generuj JWT token
const jwtToken = jwt.sign(
{
userId: user.id,
discordId: user.discord_id,
username: user.username,
role: user.role
},
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
// Przekieruj z tokenem
res.redirect(`${process.env.NODE_ENV === 'production' ? 'https://your-domain.com' : 'http://localhost:3001'}/dashboard?token=${jwtToken}`);
} catch (error) {
console.error('Błąd podczas autoryzacji Discord:', error);
res.status(500).json({ error: 'Błąd podczas autoryzacji' });
}
});
// Wylogowanie
router.post('/logout', (req, res) => {
res.json({ message: 'Wylogowano pomyślnie' });
});
// Weryfikacja tokenu
router.get('/verify', this.authenticateToken, (req, res) => {
res.json({ user: req.user });
});
return router;
}
createGuildRoutes() {
const router = express.Router();
// Pobierz serwery użytkownika
router.get('/', this.authenticateToken, async (req, res) => {
try {
// Tu powinieneś pobrać serwery z Discord API i porównać z bazą
// Na razie zwróć mockowane dane
res.json([
{
id: '123456789',
name: 'Test Server',
icon: null,
hasBot: true,
userPermissions: ['MANAGE_CHANNELS']
}
]);
} catch (error) {
console.error('Błąd podczas pobierania serwerów:', error);
res.status(500).json({ error: 'Błąd podczas pobierania serwerów' });
}
});
// Pobierz konfigurację serwera
router.get('/:guildId/config', this.authenticateToken, async (req, res) => {
try {
const { guildId } = req.params;
const welcomeChannel = await this.db.getWelcomeChannel(guildId);
const welcomeMessage = await this.db.getWelcomeMessage(guildId);
res.json({
welcomeChannel,
welcomeMessage
});
} catch (error) {
console.error('Błąd podczas pobierania konfiguracji:', error);
res.status(500).json({ error: 'Błąd podczas pobierania konfiguracji' });
}
});
return router;
}
createMessageRoutes() {
const router = express.Router();
// Pobierz wiadomość powitalną
router.get('/:guildId', this.authenticateToken, async (req, res) => {
try {
const { guildId } = req.params;
const message = await this.db.getWelcomeMessage(guildId);
res.json(message || {
content: this.getDefaultWelcomeMessage(),
embed_data: null
});
} catch (error) {
console.error('Błąd podczas pobierania wiadomości:', error);
res.status(500).json({ error: 'Błąd podczas pobierania wiadomości' });
}
});
// Zapisz wiadomość powitalną
router.post('/:guildId',
this.authenticateToken,
[
body('content').notEmpty().withMessage('Treść wiadomości nie może być pusta'),
body('content').isLength({ max: 2000 }).withMessage('Treść wiadomości nie może przekraczać 2000 znaków')
],
async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { guildId } = req.params;
const { content, embed_data } = req.body;
// Sprawdź czy wiadomość już istnieje
const existingMessage = await this.db.getWelcomeMessage(guildId);
let message;
if (existingMessage) {
// Zapisz rewizję przed aktualizacją
await this.db.saveMessageRevision(
existingMessage.id,
existingMessage.content,
existingMessage.embed_data,
req.user.userId
);
message = await this.db.updateWelcomeMessage(guildId, content, embed_data);
} else {
message = await this.db.saveWelcomeMessage(guildId, content, embed_data);
}
res.json({
message: 'Wiadomość została zapisana pomyślnie',
data: message
});
} catch (error) {
console.error('Błąd podczas zapisywania wiadomości:', error);
res.status(500).json({ error: 'Błąd podczas zapisywania wiadomości' });
}
}
);
// Historia wersji wiadomości
router.get('/:guildId/revisions', this.authenticateToken, async (req, res) => {
try {
const { guildId } = req.params;
const message = await this.db.getWelcomeMessage(guildId);
if (!message) {
return res.json([]);
}
const revisions = await this.db.getMessageRevisions(message.id);
res.json(revisions);
} catch (error) {
console.error('Błąd podczas pobierania historii:', error);
res.status(500).json({ error: 'Błąd podczas pobierania historii' });
}
});
return router;
}
// Middleware autoryzacji
authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Brak tokenu dostępu' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Nieprawidłowy token' });
}
req.user = user;
next();
});
}
// Error handler
errorHandler(error, req, res, next) {
console.error('Błąd serwera:', error);
if (error.type === 'entity.parse.failed') {
return res.status(400).json({ error: 'Nieprawidłowy format JSON' });
}
res.status(500).json({
error: 'Wewnętrzny błąd serwera',
...(process.env.NODE_ENV === 'development' && { details: error.message })
});
}
getDefaultWelcomeMessage() {
return `# 🎭 Witamy na naszym serwerze!
Miło Cię tutaj widzieć! 👋
## 📋 Najważniejsze informacje:
• Przeczytaj regulamin serwera
• Przedstaw się w odpowiednim kanale
• Baw się dobrze i szanuj innych członków
## 🎮 Funkcje serwera:
• Kanały tematyczne
• System ról
• Eventy i konkursy
---
*Ta wiadomość może być edytowana przez panel administracyjny*`;
}
async start() {
const port = process.env.API_PORT || 3000;
this.server = this.app.listen(port, () => {
console.log(`🌐 Panel web uruchomiony na porcie ${port}`);
console.log(`📱 URL panelu: http://localhost:${port}`);
});
return this.server;
}
async stop() {
if (this.server) {
this.server.close();
}
}
}
module.exports = { WebPanel };