first commit
This commit is contained in:
440
bot/backend/commands/index.js
Normal file
440
bot/backend/commands/index.js
Normal 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 };
|
||||
234
bot/backend/database/DatabaseManager.js
Normal file
234
bot/backend/database/DatabaseManager.js
Normal 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 };
|
||||
43
bot/backend/deploy-commands.js
Normal file
43
bot/backend/deploy-commands.js
Normal 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
113
bot/backend/index.js
Normal 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
368
bot/backend/web/server.js
Normal 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 };
|
||||
Reference in New Issue
Block a user